├── README.md
├── doc
└── intro.md
├── .gitignore
├── deps.edn
├── CONTRIBUTING.md
├── .github
└── workflows
│ └── test.yml
├── src
├── main
│ ├── dotnet
│ │ └── packager
│ │ │ └── clojure.tools.namespace.csproj
│ └── clojure
│ │ └── clojure
│ │ └── tools
│ │ └── namespace
│ │ ├── reload.clj
│ │ ├── parse.cljc
│ │ ├── track.cljc
│ │ ├── file.clj
│ │ ├── dependency.cljc
│ │ ├── dir.clj
│ │ ├── repl.clj
│ │ └── find.clj
└── test
│ └── clojure
│ └── clojure
│ └── tools
│ └── namespace
│ ├── track_test.clj
│ ├── parse_test1.clj
│ ├── dir_test.clj
│ ├── repl_test.clj
│ ├── find_test.clj
│ ├── reload_test.clj
│ ├── file_test.clj
│ ├── test_helpers.clj
│ ├── parse_test.clj
│ └── dependency_test.clj
├── project.clj
├── CHANGES.md
└── epl.html
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure/clr.tools.namespace/master/README.md
--------------------------------------------------------------------------------
/doc/intro.md:
--------------------------------------------------------------------------------
1 | # Introduction to clojure.tools.namespace
2 |
3 | TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/)
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | /lib/
3 | /classes/
4 | /targets/
5 | /target
6 | .cpcache
7 | .idea/
8 | *.iml
9 | /classes
10 | /checkouts
11 | *.jar
12 | *.class
13 | *.dll
14 | *.pdb
15 | *.exe
16 | .lein-deps-sum
17 | .lein-failures
18 | .lein-plugins
19 | .vs
20 |
21 | #Visual Studio artifacts
22 | bin
23 | obj
24 | *.user
25 | *.suo
26 | *.nupkg
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src/main/clojure"]
2 | :deps
3 | {io.github.clojure/clr.tools.reader {:git/tag "v1.5.2" :git/sha "1a7a8e9"}}
4 |
5 | :aliases
6 | {:test
7 | {:extra-paths ["src/test/clojure"]
8 | :extra-deps {io.github.dmiller/test-runner {:git/sha "c055ea13d19c6a9b9632aa2370fcc2215c8043c3"}}
9 | ;; :main-opts ["-m" "cognitect.test-runner" "-d" "src/test/clojure"]
10 | :exec-fn cognitect.test-runner.api/test
11 | :exec-args {:dirs ["src/test/clojure"]}}}}
12 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | This is a [Clojure contrib] project.
2 |
3 | Under the Clojure contrib [guidelines], this project cannot accept
4 | pull requests. All patches must be submitted via [JIRA].
5 |
6 | See [Contributing] on the Clojure website for
7 | more information on how to contribute.
8 |
9 | [Clojure contrib]: https://clojure.org/community/contrib_libs
10 | [Contributing]: https://clojure.org/community/contributing
11 | [JIRA]: https://clojure.atlassian.net/browse/TNS
12 | [guidelines]: https://clojure.org/community/contrib_howto
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [push, pull_request]
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: Checkout
8 | uses: actions/checkout@v3
9 |
10 | - name: Prepare dotnet
11 | uses: xt0rted/setup-dotnet@v1.5.0
12 |
13 | - name: Prepare Clojure CLR
14 | run: |
15 | dotnet tool install --global Clojure.Main --version 1.12.0-alpha10
16 | dotnet tool install --global Clojure.Cljr --version 0.1.0-alpha5
17 | - name: Run cljr tests
18 | run: cljr -X:test
--------------------------------------------------------------------------------
/src/main/dotnet/packager/clojure.tools.namespace.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;netstandard2.1
5 |
6 |
7 |
8 | clojure.tools.namespace
9 | clojure.tools
10 | clojure.tools.namespace
11 | clojure.tools.namespace
12 | clojure.tools.namespace
13 | ClojureCLR Contributors
14 | Tools for managing namespaces in ClojureCLR.
15 | Copyright © Rich Hickey, ClojureCLR Contributors 2025
16 | EPL-1.0
17 | https://github.com/clojure/clr.tools.namesapce
18 | ClojureCLR contributors
19 | Clojure;ClojureCLR
20 | 1.5.5
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/track_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.track-test
2 | (:refer-clojure :exclude (remove))
3 | (:use clojure.test
4 | clojure.tools.namespace.track))
5 |
6 | (deftest t-tracker
7 | (let [x (tracker)]
8 | (is (empty? x))
9 | (is (map? x))))
10 |
11 | (defn- equal-dep-map? [x load unload dependencies depenendents]
12 | (= x {:clojure.tools.namespace.track/load load
13 | :clojure.tools.namespace.track/unload unload
14 | :clojure.tools.namespace.track/deps (clojure.tools.namespace.dependency.MapDependencyGraph. dependencies depenendents)}))
15 |
16 |
17 | (deftest t-add
18 | (let [y (add (tracker) '{ a #{b} b #{c d}})
19 | w (add y '{a #{c} b #{e} d #{f g}})]
20 | (is (equal-dep-map? y '(b a) '(a b) '{b #{c d}, a #{b}} '{d #{b}, c #{b}, b #{a}})) ;;; ordering diff on unload: '(b a)
21 | (is (equal-dep-map? w '(a b d) '(a b d) '{d #{f g}, b #{e}, a #{c}} '{g #{d}, f #{d}, e #{b}, d #{b}, c #{b a}, b #{a}})))) ;; ordering diff on unload: '(a d b))
22 |
23 | (deftest t-remove
24 | (let [y (add (tracker) '{ a #{b} b #{c d}})
25 | z (remove y '(d))
26 | w (remove y '(q))]
27 | #_(is (equal-dep-map? z '(b a) '(a b d) '{b #{c d}, a #{b}} '{d #{b}, c #{b}, b #{a}})) ;; with the TNS-6 change, shouldn't this test change?
28 | (is (equal-dep-map? z '(b a) '(a b d) '{b #{c}, a #{b}} '{c #{b}, b #{a}}))
29 | (is (= y w))))
30 |
31 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/parse_test1.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.parse-test1
2 | (:use clojure.test
3 | clojure.tools.namespace.parse))
4 | ;;; DM: I don't know where this came from. Does not appear to be part of the current or historical c.t.namespace code
5 | (deftest t-comment?
6 | (testing "is a comment"
7 | (are [x] (comment? x)
8 | '(comment)
9 | '(comment a b c)))
10 | (testing "not a comment"
11 | (are [x] (not (comment? x))
12 | 7
13 | ()
14 | '(a b c)
15 | 'comment
16 | '[comment a b])))
17 |
18 | (deftest t-ns-decl?
19 | (testing "is an ns"
20 | (are [x] (ns-decl? x)
21 | '(ns)
22 | '(ns a b c)))
23 | (testing "not an ns"
24 | (are [x] (not (ns-decl? x))
25 | 7
26 | ()
27 | '(a b c)
28 | 'ns
29 | '[ns a b])))
30 |
31 | (defn- read-ns-decl-from-string [s]
32 | (with-open [ptr (clojure.lang.PushbackTextReader. (System.IO.StringReader. s))]
33 | (read-ns-decl ptr)))
34 |
35 | (deftest t-read-ns-decl
36 | (are [v s] (= v (read-ns-decl-from-string s))
37 | nil ""
38 | nil "(comment a b c)"
39 | nil "(a) (b) (c)"
40 | '(ns a) "(a) (ns a)"
41 | '(ns a) "(ns a) (comment (ns b))"
42 | '(ns a) "(comment (ns b)) (comment (ns c)) (ns a) (other things)"))
43 |
44 | (deftest t-deps-from-ns-decl
45 | (are [deps form] (= deps (deps-from-ns-decl form))
46 | #{'clojure.set
47 | 'clojure.zip} '(ns a (:require (clojure zip [set :as s])))
48 | #{'clojure.set} '(ns a (:require clojure.set))
49 | #{'clojure.set
50 | 'clojure.zip} '(ns a (:require clojure.set) (:use clojure.zip) (:refer clojure.whatever))))
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject org.clojure.clr/tools.namespace "1.5.5"
2 | :description "Port of clojure.org/tools.namespace to ClojureCLR"
3 | :url "https://github.com/clojure/clr.tools.namespace"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[org.clojure.clr/tools.reader "1.5.2"]]
7 | :source-paths ["src/main/clojure"]
8 | :test-paths ["src/test/clojure"]
9 | :min-lein-version "2.0.0"
10 | :plugins [[lein-clr "0.2.0"]]
11 | :deploy-repositories [["clojars" {:url "https://clojars.org/repo/"
12 | :sign-releases false}]]
13 | :clr {:cmd-templates {:clj-exe [#_"mono" [CLJCLR15_40 %1]]
14 | :clj-dep [#_"mono" ["target/clr/clj/Debug 4.0" %1]]
15 | :clj-url "https://github.com/downloads/clojure/clojure-clr/clojure-clr-1.4.0-Debug-4.0.zip"
16 | :clj-zip "clojure-clr-1.4.0-Debug-4.0.zip"
17 | :curl ["curl" "--insecure" "-f" "-L" "-o" %1 %2]
18 | :nuget-ver [#_"mono" [*PATH "nuget.exe"] "install" %1 "-Version" %2]
19 | :nuget-any [#_"mono" [*PATH "nuget.exe"] "install" %1]
20 | :unzip ["unzip" "-d" %1 %2]
21 | :wget ["wget" "--no-check-certificate" "--no-clobber" "-O" %1 %2]}
22 | ;; for automatic download/unzip of ClojureCLR,
23 | ;; 1. make sure you have curl or wget installed and on PATH,
24 | ;; 2. uncomment deps in :deps-cmds, and
25 | ;; 3. use :clj-dep instead of :clj-exe in :main-cmd and :compile-cmd
26 | :deps-cmds [; [:wget :clj-zip :clj-url] ; edit to use :curl instead of :wget
27 | ; [:unzip "../clj" :clj-zip]
28 | ]
29 | :main-cmd [:clj-exe "Clojure.Main.exe"]
30 | :compile-cmd [:clj-exe "Clojure.Compile.exe"]})
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/dir_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.dir-test
2 | (:require [clojure.test :refer [deftest is]]
3 | [clojure.tools.namespace.test-helpers :as help]
4 | [clojure.tools.namespace.dir :as dir])
5 | #_(:import
6 | (java.io File)))
7 | ;;; I don't know what the equivalent test would be for .Net.
8 | #_(defn- make-symbolic-link
9 | "Reflectively calls java.nio.file.Files/createSymbolicLink on two
10 | java.io.File arguments, to avoid a compile-time dependency on
11 | java.nio.file.Files. Returns a java.io.File."
12 | [^File link ^File target]
13 | (let [path-class (Class/forName "java.nio.file.Path")
14 | attr-class (Class/forName "java.nio.file.attribute.FileAttribute")
15 | attr-array (make-array attr-class 0)
16 | attr-array-class (.getClass attr-array)
17 | to-path (.getMethod java.io.File "toPath" (into-array Class []))
18 | to-file (.getMethod path-class "toFile" (into-array Class []))
19 | create-link (.getMethod (Class/forName "java.nio.file.Files")
20 | "createSymbolicLink"
21 | (into-array Class [path-class path-class attr-array-class]))
22 | link-path (.invoke to-path link (object-array 0))
23 | target-path (.invoke to-path target (object-array 0))
24 | link (.invoke create-link path-class (object-array [link-path target-path attr-array]))]
25 | (.invoke to-file link (object-array 0))))
26 |
27 | ;; Only run this test on Java 1.7+, where java.nio.file.Files is available.
28 | #_(when (try (Class/forName "java.nio.file.Files")
29 | (catch ClassNotFoundException _ false))
30 | (deftest t-scan-by-canonical-path
31 | (let [dir (help/create-temp-dir "t-scan-by-canonical-path")
32 | main-clj (help/create-source dir 'example.main :clj '[example.one])
33 | one-cljc (help/create-source dir 'example.one :clj)
34 | other-dir (help/create-temp-dir "t-scan-by-canonical-path-other")
35 | link (File. other-dir "link")]
36 | (make-symbolic-link link dir)
37 | (is (= (::dir/files (dir/scan-dirs {} [dir]))
38 | (::dir/files (dir/scan-dirs {} [link])))))))
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/reload.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns ^{:author "Stuart Sierra, modified for ClojureCLR by David Miller"
10 | :doc "Force reloading namespaces on demand or through a
11 | dependency tracker"}
12 | clojure.tools.namespace.reload
13 | (:require [clojure.tools.namespace.track :as track]))
14 |
15 | (defn remove-lib
16 | "Remove lib's namespace and remove lib from the set of loaded libs."
17 | [lib]
18 | (remove-ns lib)
19 | (dosync (alter @#'clojure.core/*loaded-libs* disj lib)))
20 |
21 | (defn track-reload-one
22 | "Executes the next pending unload/reload operation in the dependency
23 | tracker. Returns the updated dependency tracker. If reloading caused
24 | an error, it is captured as ::error and the namespace which caused
25 | the error is ::error-ns."
26 | [tracker]
27 | (let [{unload ::track/unload, load ::track/load} tracker]
28 | (cond
29 | (seq unload)
30 | (let [n (first unload)]
31 | (remove-lib n)
32 | (update-in tracker [::track/unload] rest))
33 | (seq load)
34 | (let [n (first load)]
35 | (try (require n :reload)
36 | (update-in tracker [::track/load] rest)
37 | (catch Exception t ;;; Throwable
38 | (assoc tracker
39 | ::error t ::error-ns n ::track/unload load))))
40 | :else
41 | tracker)))
42 |
43 | (defn track-reload
44 | "Executes all pending unload/reload operations on dependency tracker
45 | until either an error is encountered or there are no more pending
46 | operations."
47 | [tracker]
48 | (loop [tracker (dissoc tracker ::error ::error-ns)]
49 | (let [{error ::error, unload ::track/unload, load ::track/load} tracker]
50 | (if (and (not error)
51 | (or (seq load) (seq unload)))
52 | (recur (track-reload-one tracker))
53 | tracker))))
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/repl_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.repl-test
2 | (:require [clojure.test :refer [deftest is use-fixtures]]
3 | [clojure.tools.namespace.dir :as dir]
4 | [clojure.tools.namespace.find :as find]
5 | [clojure.tools.namespace.repl :as repl]
6 | [clojure.tools.namespace.test-helpers :as help]))
7 |
8 | ;; Tests contributed by Brandon Correa
9 |
10 | (defn reset-repl! []
11 | (repl/clear)
12 | (repl/set-refresh-dirs))
13 |
14 | (defn reset-repl-fixture [test-fn]
15 | (reset-repl!)
16 | (test-fn)
17 | (reset-repl!))
18 |
19 | (use-fixtures :each reset-repl-fixture)
20 |
21 | (defn current-time-millis []
22 | (long
23 | (/ (- (.-Ticks DateTime/UtcNow)
24 | (.-Ticks DateTime/UnixEpoch))
25 | TimeSpan/TicksPerMillisecond)))
26 |
27 | (deftest t-repl-scan-time-component
28 | (let [before (current-time-millis)
29 | scan (repl/scan {:platform find/clj})
30 | after (current-time-millis)
31 | time (::dir/time scan)]
32 | (is (<= before time after))
33 | (is (integer? (::dir/time scan)))))
34 |
35 | (deftest t-repl-scan-twice
36 | (let [dir (help/create-temp-dir "t-repl-scan")
37 | other-dir (help/create-temp-dir "t-repl-scan-other")
38 | main-clj (help/create-source dir 'example.main :clj '[example.one])
39 | one-cljc (help/create-source dir 'example.one :clj)
40 | _ (repl/set-refresh-dirs dir other-dir)
41 | scan-1 (repl/scan {:platform find/clj})
42 | scan-2 (repl/scan {:platform find/clj})
43 | paths-1 (map str (::dir/files scan-1))
44 | paths-2 (map str (::dir/files scan-2))
45 | paths (set paths-1)]
46 | (is (= 2 (count paths-1)))
47 | (is (= paths-1 paths-2))
48 | (is (contains? paths (str main-clj)))
49 | (is (contains? paths (str one-cljc)))))
50 |
51 | (deftest t-repl-scan-after-file-modified
52 | (let [dir (help/create-temp-dir "t-repl-scan-after-file-modified")
53 | main-clj (help/create-source dir 'example.main :clj)
54 | _ (repl/set-refresh-dirs dir)
55 | scan-1 (repl/scan {:platform find/clj})
56 | _ (System.IO.File/SetLastWriteTimeUtc (.-FullName main-clj) DateTime/UtcNow)
57 | scan-2 (repl/scan {:platform find/clj})
58 | paths-1 (map str (::dir/files scan-1))
59 | paths-2 (map str (::dir/files scan-2))
60 | paths (set paths-1)]
61 | (is (= 1 (count paths-1)))
62 | (is (= paths-1 paths-2))
63 | (is (contains? paths (str main-clj)))))
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/find_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.find-test
2 | (:require [clojure.test :refer [deftest is]]
3 | [clojure.tools.namespace.test-helpers :as help]
4 | [clojure.tools.namespace.find :as find])
5 | ) ;;; (:import (java.io File))
6 |
7 | (deftest t-find-clj-and-cljc-files
8 | "main.clj depends on one.cljc which depends on two.clj.
9 | two.cljs also exists but should not be returned"
10 | (let [dir (help/create-temp-dir "t-find-clj-and-cljc-files")
11 | main-clj (help/create-source dir 'example.main :clj '[example.one])
12 | one-cljc (help/create-source dir 'example.one :cljc '[example.two])
13 | two-clj (help/create-source dir 'example.two :clj)
14 | two-cljs (help/create-source dir 'example.two :cljs)]
15 | (is (help/same-files?
16 | [main-clj one-cljc two-clj]
17 | (find/find-sources-in-dir dir)))))
18 |
19 | (deftest t-find-cljs-and-cljc-files
20 | "main.cljs depends on one.cljc which depends on two.cljs.
21 | two.clj also exists but should not be returned"
22 | (let [dir (help/create-temp-dir "t-find-cljs-and-cljc-files")
23 | main-cljs (help/create-source dir 'example.main :cljs '[example.one])
24 | one-cljc (help/create-source dir 'example.one :cljc '[example.two])
25 | two-clj (help/create-source dir 'example.two :clj)
26 | two-cljs (help/create-source dir 'example.two :cljs)]
27 | (is (help/same-files?
28 | [main-cljs one-cljc two-cljs]
29 | (find/find-sources-in-dir dir find/cljs)))))
30 |
31 | (deftest t-find-ns-decl-meta
32 | (let [dir (help/create-temp-dir "t-find-clj-and-cljc-files")
33 | main-clj (help/create-source dir 'example.main :clj '[example.one])
34 | one-cljc (help/create-source dir 'example.one :cljc '[example.two])
35 | two-clj (help/create-source dir 'example.two :clj)
36 | two-cljs (help/create-source dir 'example.two :cljs)
37 | headless-clj (help/create-headless-source dir 'example.headless :clj)]
38 | (is (every? #{(.Name ^System.IO.DirectoryInfo dir)} ;; .getName ^java.io.File
39 | (map #(-> % second meta :dir)
40 | (find/find-ns-decls [dir]))))))
41 |
42 | ;;; DM: added
43 | (deftest t-find-cljr-and-cljc-files
44 | "main.cljr depends on one.cljc which depends on two.cljr.
45 | two.clj also exists but should not be returned"
46 | (let [dir (help/create-temp-dir "t-find-cljr-and-cljc-files")
47 | main-cljr (help/create-source dir 'example.main :cljr '[example.one])
48 | one-cljc (help/create-source dir 'example.one :cljc '[example.two])
49 | two-cljs (help/create-source dir 'example.two :cljs)
50 | two-cljr (help/create-source dir 'example.two :cljr)]
51 | (is (help/same-files?
52 | [main-cljr one-cljc two-cljr]
53 | (find/find-sources-in-dir dir find/cljr)))))
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/reload_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.reload-test
2 | (:use clojure.test
3 | clojure.tools.namespace.reload))
4 |
5 | (deftest t-remove-lib
6 | (with-bindings {#'clojure.core/*loaded-libs* (ref '#{a.b c.d})}
7 | (create-ns 'a.b)
8 | (is (find-ns 'a.b))
9 | (is (some #{'a.b} @@#'clojure.core/*loaded-libs*))
10 | (remove-lib 'a.b)
11 | (is (not (find-ns 'a.b)))
12 | (is (= @@#'clojure.core/*loaded-libs* '#{c.d}))))
13 |
14 | (defn- create-tracker [load unload]
15 | {:clojure.tools.namespace.track/load load
16 | :clojure.tools.namespace.track/unload unload
17 | :clojure.tools.namespace.track/deps (clojure.tools.namespace.dependency.MapDependencyGraph. {} {})})
18 |
19 | (defmacro with-req-rem [reqfn remfn & body]
20 | `(with-redefs [clojure.core/require ~reqfn
21 | clojure.core/remove-ns ~remfn]
22 | ~@body))
23 |
24 |
25 | (defmacro with-recording [& body]
26 | `(let [rems# (atom [])
27 | reqs# (atom [])]
28 | (with-req-rem
29 | (fn [& args#] (swap! reqs# conj args#))
30 | (fn [lib#] (swap! rems# conj lib#))
31 | (let [result# (do ~@body) ]
32 | [result# @rems# @reqs#]))))
33 |
34 |
35 |
36 | (defn- do-reload-one [load unload]
37 | (with-recording
38 | (track-reload-one (create-tracker load unload))))
39 |
40 |
41 | (defn- do-reload-one-with-load-error [load]
42 | (with-req-rem
43 | (fn [& args] (throw (Exception.)))
44 | identity
45 | (track-reload-one (create-tracker load nil))))
46 |
47 | (defn- do-reload [load unload]
48 | (with-recording
49 | (track-reload (create-tracker load unload))))
50 |
51 | (defn- do-reload-with-load-error [load error-sym]
52 | (with-req-rem
53 | (fn [lib & args] (when (= lib error-sym) (throw (Exception.))))
54 | identity
55 | (track-reload (create-tracker load nil))))
56 |
57 |
58 | (defn- equal-dep-map? [x load unload]
59 | (and (= (:clojure.tools.namespace.track/load x) load)
60 | (= (:clojure.tools.namespace.track/unload x) unload)))
61 |
62 |
63 | (deftest t-track-reload-one
64 | (testing "unload"
65 | (let [[new-tracker rems reqs] (do-reload-one '(a b) '(c d))]
66 | (is (equal-dep-map? new-tracker '(a b) '(d)))
67 | (is (= rems '[c]))
68 | (is (empty? reqs))))
69 | (testing "load"
70 | (let [[new-tracker rems reqs] (do-reload-one '(a b) '())]
71 | (is (equal-dep-map? new-tracker '(b) '()))
72 | (is (empty? rems))
73 | (is (= reqs '[(a :reload)]))))
74 | (testing "load with error"
75 | (let [new-tracker (do-reload-one-with-load-error '(a b))]
76 | (is (equal-dep-map? new-tracker '(a b) '(a b)))
77 | (is (instance? Exception (:clojure.tools.namespace.reload/error new-tracker)))
78 | (is (= (:clojure.tools.namespace.reload/error-ns new-tracker) 'a)))))
79 |
80 | (deftest t-track-reload
81 | (testing "normal"
82 | (let [[new-tracker rems reqs] (do-reload '(a b) '(c d))]
83 | (is (equal-dep-map? new-tracker '() '()))
84 | (is (= rems '[c d]))
85 | (is (= reqs '[(a :reload) (b :reload)]))))
86 | (testing "load with error"
87 | (let [new-tracker (do-reload-with-load-error '(a b c) 'b)]
88 | (is (equal-dep-map? new-tracker '(b c) '(b c)))
89 | (is (instance? Exception (:clojure.tools.namespace.reload/error new-tracker)))
90 | (is (= (:clojure.tools.namespace.reload/error-ns new-tracker) 'b)))))
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/file_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.file-test
2 | (:require [clojure.test :refer [deftest is]]
3 | [clojure.tools.namespace.dependency :as dep]
4 | [clojure.tools.namespace.file :as file]
5 | [clojure.tools.namespace.test-helpers :as help]
6 | [clojure.tools.namespace.track :as track])
7 | (:import (System.IO FileInfo)))
8 |
9 | ;; Tests compliments of Brandon Correa
10 |
11 | (deftest t-add-no-files
12 | (let [tracker (file/add-files (track/tracker) nil)]
13 | (is (= (dep/->MapDependencyGraph {} {}) (::track/deps tracker)))
14 | (is (= {} (::file/filemap tracker)))
15 | (is (= '() (::track/unload tracker)))
16 | (is (= '() (::track/load tracker)))))
17 |
18 | (deftest t-add-one-file
19 | (let [dir (help/create-temp-dir "t-add-one-file")
20 | one-clj (help/create-source dir 'example.one :clj)
21 | tracker (file/add-files (track/tracker) [one-clj])]
22 | (is (= (dep/->MapDependencyGraph {} {}) (::track/deps tracker)))
23 | (is (= {one-clj 'example.one} (::file/filemap tracker)))
24 | (is (= (list 'example.one) (::track/unload tracker)))
25 | (is (= (list 'example.one) (::track/load tracker)))))
26 |
27 | (deftest t-add-file-with-dependency
28 | (let [dir (help/create-temp-dir "t-add-file-with-dependency")
29 | main-clj (help/create-source dir 'example.main :clj '[example.one])
30 | tracker (file/add-files (track/tracker) [main-clj])]
31 | (is (= {'example.main #{'example.one}} (:dependencies (::track/deps tracker))))
32 | (is (= {'example.one #{'example.main}} (:dependents (::track/deps tracker))))
33 | (is (= {main-clj 'example.main} (::file/filemap tracker)))
34 | (is (= (list 'example.main) (::track/unload tracker)))
35 | (is (= (list 'example.main) (::track/load tracker)))))
36 |
37 | (deftest t-add-file-that-already-exists
38 | (let [dir (help/create-temp-dir "t-add-file-that-already-exists")
39 | file-ref-1 (help/create-source dir 'example.main :clj)
40 | file-ref-2 (FileInfo. (.-FullName file-ref-1))
41 | tracker (-> (track/tracker)
42 | (file/add-files [file-ref-1])
43 | (file/add-files [file-ref-2]))]
44 | (is (= {} (:dependencies (::track/deps tracker))))
45 | (is (= {} (:dependents (::track/deps tracker))))
46 | (is (= {file-ref-2 'example.main} (::file/filemap tracker)))
47 | (is (= (list 'example.main) (::track/unload tracker)))
48 | (is (= (list 'example.main) (::track/load tracker)))))
49 |
50 | (deftest t-add-file-that-already-exists-in-the-same-call
51 | (let [dir (help/create-temp-dir "t-add-file-that-already-exists-in-the-same-call")
52 | file-ref-1 (help/create-source dir 'example.main :clj)
53 | file-ref-2 (FileInfo. (.-FullName file-ref-1))
54 | tracker (-> (track/tracker)
55 | (file/add-files [file-ref-1 file-ref-2]))]
56 | (is (= {} (:dependencies (::track/deps tracker))))
57 | (is (= {} (:dependents (::track/deps tracker))))
58 | (is (= {file-ref-2 'example.main} (::file/filemap tracker)))
59 | (is (= (list 'example.main) (::track/unload tracker)))
60 | (is (= (list 'example.main) (::track/load tracker)))))
61 |
62 | (deftest t-remove-no-files-from-empty-tracker
63 | (let [tracker (file/remove-files {} nil)]
64 | (is (= (dep/->MapDependencyGraph {} {}) (::track/deps tracker)))
65 | (is (nil? (::file/filemap tracker)))
66 | (is (= '() (::track/unload tracker)))
67 | (is (= '() (::track/load tracker)))))
68 |
69 | (deftest t-remove-file-with-dependency-from-filemap
70 | (let [dir (help/create-temp-dir "t-remove-file-with-dependency-from-filemap")
71 | file-ref-1 (help/create-source dir 'example.main :clj '[example.one])
72 | file-ref-2 (FileInfo. (.-FullName file-ref-1))
73 | tracker (-> (track/tracker)
74 | (file/add-files [file-ref-1])
75 | (file/remove-files [file-ref-2]))]
76 | (is (= {} (:dependencies (::track/deps tracker))))
77 | (is (= {'example.one #{}} (:dependents (::track/deps tracker))))
78 | (is (= {} (::file/filemap tracker)))
79 | (is (= (list 'example.main) (::track/unload tracker)))
80 | (is (= (list) (::track/load tracker)))))
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/parse.cljc:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns ^{:author "Stuart Sierra, modified for ClojureCLR by David Miller"
10 | :doc "Parse Clojure namespace (ns) declarations and extract
11 | dependencies."}
12 | clojure.tools.namespace.parse
13 | (:require #?(:clj [clojure.tools.reader :as reader] :cljr [clojure.tools.reader :as reader] ;; DM: Added :cljr entry
14 | :cljs [cljs.tools.reader :as reader])
15 | [clojure.set :as set]))
16 |
17 | (defn comment?
18 | "Returns true if form is a (comment ...)"
19 | [form]
20 | (and (list? form) (= 'comment (first form))))
21 |
22 | (defn ns-decl?
23 | "Returns true if form is a (ns ...) declaration."
24 | [form]
25 | (and (list? form) (= 'ns (first form))))
26 |
27 | (def clj-read-opts
28 | "Map of options for tools.reader/read allowing reader conditionals
29 | with the :clj feature enabled."
30 | {:read-cond :allow
31 | :features #{:clj}})
32 |
33 | (def cljs-read-opts
34 | "Map of options for tools.reader/read allowing reader conditionals
35 | with the :cljs feature enabled."
36 | {:read-cond :allow
37 | :features #{:cljs}})
38 |
39 | ;; DM: Added
40 | (def cljr-read-opts
41 | "Map of options for tools.reader/read allowing reader conditionals
42 | with the :cljr feature enabled."
43 | {:read-cond :allow
44 | :features #{:cljr}})
45 |
46 | (defn read-ns-decl
47 | "Attempts to read a (ns ...) declaration from a reader, and returns
48 | the unevaluated form. Returns the first top-level ns form found.
49 | Returns nil if ns declaration cannot be found. Throws exception on
50 | invalid syntax.
51 |
52 | Note that read can execute code (controlled by
53 | tools.reader/*read-eval*), and as such should be used only with
54 | trusted sources. read-opts is passed through to tools.reader/read,
55 | defaults to clj-read-opts"
56 | ([rdr]
57 | (read-ns-decl rdr nil))
58 | ([rdr read-opts]
59 | (let [opts (assoc (or read-opts cljr-read-opts) ;;; clj-read-opts
60 | :eof ::eof)]
61 | (loop []
62 | (let [form (reader/read opts rdr)]
63 | (cond
64 | (ns-decl? form) form
65 | (= ::eof form) nil
66 | :else (recur)))))))
67 |
68 |
69 | ;;; Parsing dependencies
70 |
71 | (defn- prefix-spec?
72 | "Returns true if form represents a libspec prefix list like
73 | (prefix name1 name1) or [com.example.prefix [name1 :as name1]]"
74 | [form]
75 | (and (sequential? form) ; should be a list, but often is not
76 | (symbol? (first form))
77 | (not-any? keyword? form)
78 | (< 1 (count form)))) ; not a bare vector like [foo]
79 |
80 | (defn- option-spec?
81 | "Returns true if form represents a libspec vector containing optional
82 | keyword arguments like [namespace :as alias] or
83 | [namespace :refer (x y)] or just [namespace]"
84 | [form]
85 | (and (sequential? form) ; should be a vector, but often is not
86 | (or (symbol? (first form))
87 | (string? (first form)))
88 | (or (keyword? (second form)) ; vector like [foo :as f]
89 | (= 1 (count form))))) ; bare vector like [foo]
90 |
91 | (defn- deps-from-libspec [prefix form]
92 | (cond (prefix-spec? form)
93 | (mapcat (fn [f] (deps-from-libspec
94 | (symbol (str (when prefix (str prefix "."))
95 | (first form)))
96 | f))
97 | (rest form))
98 | (option-spec? form)
99 | (when-not (= :as-alias (second form))
100 | (deps-from-libspec prefix (first form)))
101 | (symbol? form)
102 | (list (symbol (str (when prefix (str prefix ".")) form)))
103 | (keyword? form) ; Some people write (:require ... :reload-all)
104 | nil
105 | (string? form) ; NPM dep, ignore
106 | nil
107 | :else
108 | (throw (ex-info "Unparsable namespace form"
109 | {:reason ::unparsable-ns-form
110 | :form form}))))
111 |
112 | (def ^:private ns-clause-head-names
113 | "Set of symbol/keyword names which can appear as the head of a
114 | clause in the ns form."
115 | #{"use" "require" "require-macros"})
116 |
117 | (def ^:private ns-clause-heads
118 | "Set of all symbols and keywords which can appear at the head of a
119 | dependency clause in the ns form."
120 | (set (mapcat (fn [name] (list (keyword name)
121 | (symbol name)))
122 | ns-clause-head-names)))
123 |
124 | (defn- deps-from-ns-form [form]
125 | (when (and (sequential? form) ; should be list but sometimes is not
126 | (contains? ns-clause-heads (first form)))
127 | (mapcat #(deps-from-libspec nil %) (rest form))))
128 |
129 | (defn name-from-ns-decl
130 | "Given an (ns...) declaration form (unevaluated), returns the name
131 | of the namespace as a symbol."
132 | [decl]
133 | (second decl))
134 |
135 | (defn deps-from-ns-decl
136 | "Given an (ns...) declaration form (unevaluated), returns a set of
137 | symbols naming the dependencies of that namespace. Handles :use and
138 | :require clauses but not :load."
139 | [decl]
140 | (set (mapcat deps-from-ns-form decl)))
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/test_helpers.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.test-helpers
2 | "Utilities to help with testing files and namespaces."
3 | (:require [clojure.clr.io :as io] ;;; clojure.java.io
4 | [clojure.string :as string])
5 | (:import (System.IO Path File FileInfo DirectoryInfo TextWriter Directory))) ;;; (java.io Closeable File Writer PrintWriter)
6 |
7 | (defn create-temp-dir
8 | "Creates and returns a new temporary directory java.io.File."
9 | [name]
10 | (let [temp-filename (Path/Combine (Path/GetTempPath) name) ;;; temp-file (File/createTempFile name nil)
11 | _ (when (or (File/Exists temp-filename)(Directory/Exists temp-filename))
12 | (Directory/Delete temp-filename true)) ;;; (.delete temp-file)
13 | dir (Directory/CreateDirectory temp-filename)] ;;; (.mkdirs temp-file)
14 | (println "Temporary directory" (Path/GetFullPath temp-filename)) ;;; (.getAbsolutePath temp-file)
15 | dir)) ;;; temp-file
16 |
17 | (defn- write-contents
18 | "Writes contents into writer. Strings are written as-is via println,
19 | other types written as by prn."
20 | [^TextWriter writer contents] ;;; ^Writer
21 | {:pre [(sequential? contents)]}
22 | (binding [*out* writer] ;;; (PrintWriter. writer)
23 | (doseq [content contents]
24 | (if (string? content)
25 | (println content)
26 | (prn content))
27 | (newline))))
28 |
29 | ;;; DM: Added
30 | (defn- coerce-to-file-path
31 | "Creates a path (string) from a vector of strings and possible a beginning DirectoryInfo"
32 | [path]
33 | (let [coerce (fn [x]
34 | (cond
35 | (instance? DirectoryInfo x) (.FullName ^DirectoryInfo x)
36 | (instance? FileInfo x) (.FullName ^FileInfo x)
37 | (instance? String x) x
38 | :otherwise (str x)))]
39 | (Path/Combine (into-array String (map coerce path)))))
40 | ;;; DM:
41 |
42 | (defn create-file
43 | "Creates a file from a vector of path elements. Writes contents into
44 | the file. Elements of contents may be data, written via prn, or
45 | strings, written directly."
46 | [path contents]
47 | {:pre [(vector? path)]}
48 | (let [path (coerce-to-file-path path) ;;; ^File file (apply io/file path)
49 | ^FileInfo fi (FileInfo. path)] ;;; (when-let [parent (.getParentFile file)]
50 | (when-not (.Exists fi) (Directory/CreateDirectory (.. fi Directory FullName))) ;;; (.mkdirs parent))
51 | (with-open [wtr (io/text-writer fi)] ;;; io/writer file
52 | (write-contents wtr contents))
53 | fi)) ;;; file
54 |
55 | (defn- sym->path
56 | "Converts a symbol name into a vector of path parts, not including
57 | file extension."
58 | [symbol]
59 | {:pre [(symbol? symbol)]
60 | :post [(vector? %)]}
61 | (-> (name symbol)
62 | (string/replace \- \_)
63 | (string/split #"\.")))
64 |
65 | (defn- source-path
66 | "Returns a vector of path components for namespace named sym,
67 | with given file extension (keyword)."
68 | [sym extension]
69 | (let [path (sym->path sym)
70 | basename (peek path)
71 | filename (str basename \. (name extension))]
72 | (conj (pop path) filename)))
73 |
74 | (defn create-source
75 | "Creates a file at the correct path under base-dir for a namespace
76 | named sym, with file extension (keyword), containing a ns
77 | declaration which :require's the dependencies (symbols). Optional
78 | contents written after the ns declaration as by write-contents."
79 | ([base-dir sym extension]
80 | (create-source base-dir sym extension nil nil))
81 | ([base-dir sym extension dependencies]
82 | (create-source base-dir sym extension dependencies nil))
83 | ([base-dir sym extension dependencies contents]
84 | (let [full-path (into [base-dir] (source-path sym extension))
85 | ns-decl (if (seq dependencies)
86 | (list 'ns sym (list* :require dependencies))
87 | (list 'ns sym))]
88 | (create-file full-path (into [ns-decl] contents)))))
89 |
90 | (defn create-headless-source
91 | "Creates a file at the correct path under base-dir for a file that
92 | declares in-ns for namespace named sym, with file extension (keyword)
93 | and will also require the dependencies (symbols). Optional contents
94 | written after the ns declaration as by write-contents."
95 | ([base-dir sym extension]
96 | (create-headless-source base-dir sym extension nil nil))
97 | ([base-dir sym extension dependencies]
98 | (create-headless-source base-dir sym extension dependencies nil))
99 | ([base-dir sym extension dependencies contents]
100 | (let [full-path (into [base-dir] (source-path sym extension))
101 | ins-decl (list 'in-ns (list 'quote sym))
102 | deps-decl (when (seq dependencies)
103 | (map #(list 'require `(quote ~%)) dependencies))]
104 | (create-file full-path (filter identity (concat [ins-decl] deps-decl contents))))))
105 |
106 | (defn same-files?
107 | "True if files-a and files-b contain the same canonical File's,
108 | regardless of order."
109 | [files-a files-b]
110 | (= (sort (map #(.FullName ^FileInfo %) files-a)) ;;; .getCanonicalPath ^File
111 | (sort (map #(.FullName ^FileInfo %) files-b)))) ;;; .getCanonicalPath ^File
112 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/track.cljc:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns ^{:author "Stuart Sierra"
10 | :doc "Dependency tracker which can compute which namespaces need to be
11 | reloaded after files have changed. This is the low-level
12 | implementation that requires you to find the namespace dependencies
13 | yourself: most uses will interact with the wrappers in
14 | clojure.tools.namespace.file and clojure.tools.namespace.dir or the
15 | public API in clojure.tools.namespace.repl."}
16 | clojure.tools.namespace.track
17 | (:refer-clojure :exclude (remove))
18 | (:require [clojure.set :as set]
19 | [clojure.tools.namespace.dependency :as dep]))
20 |
21 | (defn- remove-deps [deps names]
22 | (reduce dep/remove-node deps names))
23 |
24 | (defn- add-deps [deps depmap]
25 | (reduce (fn [ds [name dependencies]]
26 | (reduce (fn [g dep] (dep/depend g name dep))
27 | ds dependencies))
28 | deps depmap))
29 |
30 | (defn- update-deps [deps depmap]
31 | (-> deps
32 | (remove-deps (keys depmap))
33 | (add-deps depmap)))
34 |
35 | (defn- affected-namespaces [deps names]
36 | (set/union (set names)
37 | (dep/transitive-dependents-set deps names)))
38 |
39 | (defn add
40 | "Returns an updated dependency tracker with new/updated namespaces.
41 |
42 | Depmap is a map describing the new or modified namespaces. Keys in
43 | the map are namespace names (symbols). Values in the map are sets of
44 | symbols naming the direct dependencies of each namespace. For
45 | example, assuming these ns declarations:
46 |
47 | (ns alpha (:require beta))
48 | (ns beta (:require gamma delta))
49 |
50 | the depmap would look like this:
51 |
52 | {alpha #{beta}
53 | beta #{gamma delta}}
54 |
55 | After adding new/updated namespaces, the dependency tracker will
56 | have two lists associated with the following keys:
57 |
58 | :clojure.tools.namespace.track/unload
59 | is the list of namespaces that need to be removed
60 |
61 | :clojure.tools.namespace.track/load
62 | is the list of namespaces that need to be reloaded
63 |
64 | To reload namespaces in the correct order, first remove/unload all
65 | namespaces in the 'unload' list, then (re)load all namespaces in the
66 | 'load' list. The clojure.tools.namespace.reload namespace has
67 | functions to do this."
68 | [tracker depmap]
69 | (let [{load ::load
70 | unload ::unload
71 | deps ::deps
72 | :or {load (), unload (), deps (dep/graph)}} tracker
73 | new-deps (update-deps deps depmap)
74 | changed (affected-namespaces new-deps (keys depmap))
75 | ;; With a new tracker, old dependencies are empty, so
76 | ;; unload order will be undefined unless we use new
77 | ;; dependencies (TNS-20).
78 | old-deps (if (empty? (dep/nodes deps)) new-deps deps)]
79 | (assoc tracker
80 | ::deps new-deps
81 | ::unload (distinct
82 | (concat (reverse (sort (dep/topo-comparator old-deps) changed))
83 | unload))
84 | ::load (distinct
85 | (concat (sort (dep/topo-comparator new-deps) changed)
86 | load)))))
87 |
88 | (defn remove
89 | "Returns an updated dependency tracker from which the namespaces
90 | (symbols) have been removed. The ::unload and ::load lists are
91 | populated as with 'add'."
92 | [tracker names]
93 | (let [{load ::load
94 | unload ::unload
95 | deps ::deps
96 | :or {load (), unload (), deps (dep/graph)}} tracker
97 | known (set (dep/nodes deps))
98 | removed-names (filter known names)
99 | new-deps (reduce dep/remove-all deps removed-names)
100 | changed (affected-namespaces deps removed-names)]
101 | (assoc tracker
102 | ::deps new-deps
103 | ::unload (distinct
104 | (concat (reverse (sort (dep/topo-comparator deps) changed))
105 | unload))
106 | ::load (distinct
107 | (filter (complement (set removed-names))
108 | (concat (sort (dep/topo-comparator new-deps) changed)
109 | load))))))
110 |
111 | (defn tracker
112 | "Returns a new, empty dependency tracker"
113 | []
114 | {})
115 |
116 | (comment
117 | ;; Structure of the namespace tracker map. Documented for reference
118 | ;; only: This is not a public API.
119 |
120 | {;; Dependency graph of namespace names (symbols) as defined in
121 | ;; clojure.tools.namespace.dependency/graph
122 | :clojure.tools.namespace.track/deps {}
123 |
124 | ;; Ordered list of namespace names (symbols) that need to be
125 | ;; removed to bring the running system into agreement with the
126 | ;; source files.
127 | :clojure.tools.namespace.track/unload ()
128 |
129 | ;; Ordered list of namespace names (symbols) that need to be
130 | ;; (re)loaded to bring the running system into agreement with the
131 | ;; source files.
132 | :clojure.tools.namespace.track/load ()
133 |
134 | ;; Added by clojure.tools.namespace.file: Map from source files
135 | ;; (java.io.File) to the names (symbols) of namespaces they
136 | ;; represent.
137 | :clojure.tools.namespace.file/filemap {}
138 |
139 | ;; Added by clojure.tools.namespace.dir: Set of source files
140 | ;; (java.io.File) which have been seen by this dependency tracker;
141 | ;; used to determine when files have been deleted.
142 | :clojure.tools.namespace.dir/files #{}
143 |
144 | ;; Added by clojure.tools.namespace.dir: Instant when the
145 | ;; directories were last scanned, as returned by
146 | ;; System/currentTimeMillis.
147 | :clojure.tools.namespace.dir/time 1405201862262})
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/file.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns ^{:author "Stuart Sierra, modified for ClojureCLR by David Miller"
10 | :doc "Read and track namespace information from files"}
11 | clojure.tools.namespace.file
12 | (:require [clojure.clr.io :as io] ;;; clojure.java.io
13 | [clojure.tools.namespace.parse :as parse]
14 | [clojure.tools.namespace.track :as track])
15 | (:import (clojure.lang PushbackTextReader))) ;;; (java.io PushbackReader)))
16 |
17 | (defn read-file-ns-decl
18 | "Attempts to read a (ns ...) declaration from file, and returns the
19 | unevaluated form. Returns nil if ns declaration cannot be found.
20 | read-opts is passed through to tools.reader/read."
21 | ([file]
22 | (read-file-ns-decl file nil))
23 | ([file read-opts]
24 | (with-open [rdr (PushbackTextReader. (io/text-reader file))] ;;; PushbackReader. io/reader
25 | (parse/read-ns-decl rdr read-opts))))
26 |
27 | (declare is-file? is-directory?)
28 |
29 | (defn file-with-extension?
30 | "Returns true if the java.io.File represents a file whose name ends
31 | with one of the Strings in extensions."
32 | {:added "0.3.0"}
33 | [^System.IO.FileInfo file extensions] ;;; ^java.io.File
34 | (and (is-file? file) ;;; (.isFile file) java.io.File conflates regular files and directors. Not so with FileInfo
35 | (let [extn (.Extension file)] ;;; name (.getName file)
36 | (some #(= extn %) extensions)))) ;;; #(.endsWith name %)
37 |
38 | (def ^{:added "0.3.0"}
39 | clojure-extensions
40 | "File extensions for Clojure (JVM) files."
41 | (list ".clj" ".cljc"))
42 |
43 | (def ^{:added "0.3.0"}
44 | clojurescript-extensions
45 | "File extensions for ClojureScript files."
46 | (list ".cljs" ".cljc"))
47 |
48 | (def ^{:added "0.3.0"}
49 | clojure-clr-extensions
50 | "File extensions for Clojure (CLR) files."
51 | (list ".cljr" ".cljc" ".clj" ))
52 |
53 | (defn clojure-file?
54 | "Returns true if the java.io.File represents a file which will be
55 | read by the Clojure (JVM) compiler."
56 | [^System.IO.FileSystemInfo file] ;;; java.io.File
57 | (file-with-extension? file clojure-extensions))
58 |
59 | (defn clojurescript-file?
60 | "Returns true if the java.io.File represents a file which will be
61 | read by the Clojure (JVM) compiler."
62 | [^System.IO.FileSystemInfo file] ;;; java.io.File
63 | (file-with-extension? file clojurescript-extensions))
64 |
65 | (defn clojure-clr-file?
66 | "Returns true if the java.io.File represents a file which will be
67 | read by the Clojure (JVM) compiler."
68 | [^System.IO.FileSystemInfo file] ;;; java.io.File
69 | (file-with-extension? file clojure-clr-extensions))
70 |
71 | ;; Dealing with FileInfo.Equals is reference-based, not structuaral -- via Brandon Correa
72 |
73 | (defn- files= [file-1 file-2]
74 | (= (.-FullName file-1) (.-FullName file-2)))
75 |
76 | (defn some-file [coll file]
77 | (reduce #(when (files= file %2) (reduced %2)) nil coll))
78 |
79 | (defn into-files [files others]
80 | (into files (remove #(some-file files %) others)))
81 |
82 | (defn- dissoc-files [m files]
83 | (when m
84 | (select-keys m (remove #(some-file files %) (keys m)))))
85 |
86 | (defn- get-file [filemap file]
87 | (reduce #(when (files= file (first %2)) (reduced (second %2))) nil filemap))
88 |
89 | (defn- files->symbols [tracker files]
90 | (let [filemap (::filemap tracker {})]
91 | (keep #(get-file filemap %) files)))
92 |
93 | (defn- merge-file-map [m other]
94 | (merge (dissoc-files m (keys other)) other))
95 |
96 | (defn- distinct-files [files]
97 | (reduce #(-> (disj %1 (some-file %1 %2))
98 | (conj %2)) #{} files))
99 |
100 | ;;
101 |
102 | ;;; Dependency tracker
103 |
104 | (defn- files-and-deps [files read-opts]
105 | (reduce (fn [m file]
106 | (if-let [decl (read-file-ns-decl file read-opts)]
107 | (let [deps (parse/deps-from-ns-decl decl)
108 | name (parse/name-from-ns-decl decl)]
109 | (-> m
110 | (assoc-in [:depmap name] deps)
111 | (assoc-in [:filemap file] name)))
112 | m))
113 | {} (distinct-files files))) ;;; files
114 |
115 |
116 | (def ^:private merge-map (fnil merge-file-map {})) ;;; (fnil merge {})
117 |
118 | (defn add-files
119 | "Reads ns declarations from files; returns an updated dependency
120 | tracker with those files added. read-opts is passed through to
121 | tools.reader."
122 | ([tracker files]
123 | (add-files tracker files nil))
124 | ([tracker files read-opts]
125 | (let [{:keys [depmap filemap]} (files-and-deps files read-opts)]
126 | (-> tracker
127 | (track/add depmap)
128 | (update ::filemap merge-map filemap))))) ;;; (update-in [::filemap] merge-map filemap)
129 |
130 | (defn remove-files
131 | "Returns an updated dependency tracker with files removed. The files
132 | must have been previously added with add-files."
133 | [tracker files]
134 | (-> tracker
135 | (track/remove (files->symbols tracker files)) ;;; (track/remove (keep (::filemap tracker {}) files))
136 | (update ::filemap dissoc-files files))) ;;; (update-in [::filemap] #(apply dissoc % files))
137 |
138 | ;;; Added
139 |
140 | (defn is-file? [^System.IO.FileSystemInfo file]
141 | (not= (enum-and (.Attributes file) System.IO.FileAttributes/Directory) System.IO.FileAttributes/Directory))
142 |
143 | (defn is-directory? [^System.IO.FileSystemInfo file]
144 | (= (enum-and (.Attributes file) System.IO.FileAttributes/Directory) System.IO.FileAttributes/Directory))
145 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/dependency.cljc:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns ^{:author "Stuart Sierra, modified for ClojureCLR by David Miller"
10 | :doc "Bidirectional graphs of dependencies and dependent objects."}
11 | clojure.tools.namespace.dependency
12 | (:require [clojure.set :as set]))
13 |
14 | (defprotocol DependencyGraph
15 | (immediate-dependencies [graph node]
16 | "Returns the set of immediate dependencies of node.")
17 | (immediate-dependents [graph node]
18 | "Returns the set of immediate dependents of node.")
19 | (transitive-dependencies [graph node]
20 | "Returns the set of all things which node depends on, directly or
21 | transitively.")
22 | (transitive-dependencies-set [graph node-set]
23 | "Returns the set of all things which any node in node-set depends
24 | on, directly or transitively.")
25 | (transitive-dependents [graph node]
26 | "Returns the set of all things which depend upon node, directly or
27 | transitively.")
28 | (transitive-dependents-set [graph node-set]
29 | "Returns the set of all things which depend upon any node in
30 | node-set, directly or transitively.")
31 | (nodes [graph]
32 | "Returns the set of all nodes in graph."))
33 |
34 | (defprotocol DependencyGraphUpdate
35 | (depend [graph node dep]
36 | "Returns a new graph with a dependency from node to dep (\"node depends
37 | on dep\"). Forbids circular dependencies.")
38 | (remove-edge [graph node dep]
39 | "Returns a new graph with the dependency from node to dep removed.")
40 | (remove-all [graph node]
41 | "Returns a new dependency graph with all references to node removed.")
42 | (remove-node [graph node]
43 | "Removes the node from the dependency graph without removing it as a
44 | dependency of other nodes. That is, removes all outgoing edges from
45 | node."))
46 |
47 | (defn- remove-from-map [amap x]
48 | (reduce (fn [m [k vs]]
49 | (assoc m k (disj vs x)))
50 | {} (dissoc amap x)))
51 |
52 | (defn- transitive
53 | "Recursively expands the set of dependency relationships starting
54 | at (get neighbors x), for each x in node-set"
55 | [neighbors node-set]
56 | (loop [unexpanded (mapcat neighbors node-set)
57 | expanded #{}]
58 | (if-let [[node & more] (seq unexpanded)]
59 | (if (contains? expanded node)
60 | (recur more expanded)
61 | (recur (concat more (neighbors node))
62 | (conj expanded node)))
63 | expanded)))
64 |
65 | (declare depends?)
66 |
67 | (def set-conj (fnil conj #{}))
68 |
69 | (defrecord MapDependencyGraph [dependencies dependents]
70 | DependencyGraph
71 | (immediate-dependencies [graph node]
72 | (get dependencies node #{}))
73 | (immediate-dependents [graph node]
74 | (get dependents node #{}))
75 | (transitive-dependencies [graph node]
76 | (transitive dependencies #{node}))
77 | (transitive-dependencies-set [graph node-set]
78 | (transitive dependencies node-set))
79 | (transitive-dependents [graph node]
80 | (transitive dependents #{node}))
81 | (transitive-dependents-set [graph node-set]
82 | (transitive dependents node-set))
83 | (nodes [graph]
84 | (clojure.set/union (set (keys dependencies))
85 | (set (keys dependents))))
86 | DependencyGraphUpdate
87 | (depend [graph node dep]
88 | (when (or (= node dep) (depends? graph dep node))
89 | (throw (ex-info (str "Circular dependency between "
90 | (pr-str node) " and " (pr-str dep))
91 | {:reason ::circular-dependency
92 | :node node
93 | :dependency dep})))
94 | (MapDependencyGraph.
95 | (update-in dependencies [node] set-conj dep)
96 | (update-in dependents [dep] set-conj node)))
97 | (remove-edge [graph node dep]
98 | (MapDependencyGraph.
99 | (update-in dependencies [node] disj dep)
100 | (update-in dependents [dep] disj node)))
101 | (remove-all [graph node]
102 | (MapDependencyGraph.
103 | (remove-from-map dependencies node)
104 | (remove-from-map dependents node)))
105 | (remove-node [graph node]
106 | (MapDependencyGraph.
107 | (dissoc dependencies node)
108 | dependents)))
109 |
110 | (defn graph "Returns a new, empty, dependency graph." []
111 | (->MapDependencyGraph {} {}))
112 |
113 | (defn depends?
114 | "True if x is directly or transitively dependent on y."
115 | [graph x y]
116 | (contains? (transitive-dependencies graph x) y))
117 |
118 | (defn dependent?
119 | "True if y is a dependent of x."
120 | [graph x y]
121 | (contains? (transitive-dependents graph x) y))
122 |
123 | (defn topo-sort
124 | "Returns a topologically-sorted list of nodes in graph."
125 | [graph]
126 | (loop [sorted ()
127 | g graph
128 | todo (set (filter #(empty? (immediate-dependents graph %))
129 | (nodes graph)))]
130 | (if (empty? todo)
131 | sorted
132 | (let [[node & more] (seq todo)
133 | deps (immediate-dependencies g node)
134 | [add g'] (loop [deps deps
135 | g g
136 | add #{}]
137 | (if (seq deps)
138 | (let [d (first deps)
139 | g' (remove-edge g node d)]
140 | (if (empty? (immediate-dependents g' d))
141 | (recur (rest deps) g' (conj add d))
142 | (recur (rest deps) g' add)))
143 | [add g]))]
144 | (recur (cons node sorted)
145 | (remove-node g' node)
146 | (clojure.set/union (set more) (set add)))))))
147 |
148 | (def ^:private max-number
149 | #?(:clj Long/MAX_VALUE :cljr Int64/MaxValue ;;; DM: Added :cljr entry
150 | :cljs js/Number.MAX_VALUE))
151 |
152 | (defn topo-comparator
153 | "Returns a comparator fn which produces a topological sort based on
154 | the dependencies in graph. Nodes not present in the graph will sort
155 | after nodes in the graph."
156 | [graph]
157 | (let [pos (zipmap (topo-sort graph) (range))]
158 | (fn [a b]
159 | (compare (get pos a max-number)
160 | (get pos b max-number)))))
161 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/dir.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns ^{:author "Stuart Sierra, modified for ClojureCLR by David Miller"
10 | :doc "Track namespace dependencies and changes by monitoring
11 | file-modification timestamps"}
12 | clojure.tools.namespace.dir
13 | (:require [clojure.tools.namespace.file :as file]
14 | [clojure.tools.namespace.find :as find]
15 | [clojure.tools.namespace.track :as track]
16 | ;;;[clojure.java.classpath :refer [classpath-directories]]
17 | [clojure.clr.io :as io] ;;; clojure.java.io
18 | [clojure.set :as set]
19 | [clojure.string :as string])
20 | (:import (System.IO DirectoryInfo FileSystemInfo Path) (System.Text.RegularExpressions Regex))) ;;; (java.io File) (java.util.regex Pattern)
21 |
22 | (declare make-dir-info)
23 |
24 | (defn- find-files [dirs platform]
25 | (->> dirs
26 | (map make-dir-info) ;;; (map io/file)
27 | ;;; ;;; I have no idea if this is necessary. (map #(.getCanonicalFile ^File %))
28 | (filter #(.Exists ^DirectoryInfo %)) ;;; #(.exists ^File %)
29 | (mapcat #(find/find-sources-in-dir % platform))
30 | )) ;;; ditto: (map #(.getCanonicalFile ^File %))
31 |
32 | (defn- milliseconds-since-epoch [^DateTime time]
33 | (long
34 | (/ (- (.-Ticks time)
35 | (.-Ticks DateTime/UnixEpoch))
36 | TimeSpan/TicksPerMillisecond)))
37 |
38 | (defn- modified-files [tracker files]
39 | (filter #(< (::time tracker 0) (milliseconds-since-epoch (.-LastWriteTimeUtc ^FileSystemInfo %))) files)) ;;; #(< (::time tracker 0) (.lastModified ^File %))
40 |
41 | (defn- deleted-files [tracker files]
42 | (set (remove #(file/some-file files %) (::files tracker #{})))) ;;; (set/difference (::files tracker #{}) (set files))
43 |
44 | (defn- update-files [tracker deleted modified {:keys [read-opts]}]
45 | (let [now (milliseconds-since-epoch DateTime/UtcNow)] ;;; (System/currentTimeMillis)
46 | (-> tracker
47 | (update-in [::files] #(if % (apply disj % deleted) #{}))
48 | (file/remove-files deleted)
49 | (update-in [::files] file/into-files modified) ;;; (update-in [::files] into modified)
50 | (file/add-files modified read-opts)
51 | (assoc ::time now))))
52 |
53 | (defn- dirs-on-classpath [] ;;; This has been replaced in JVM by a call to clojure.java.classpath/classpath-directories. We don't have that, so we're leaving this in
54 | (filter file/is-directory? ;;; #(.isDirectory ^File %)
55 | (map #(DirectoryInfo. ^String %) ;;; #(File. ^String %)
56 | (string/split
57 | (Environment/GetEnvironmentVariable "CLOJURE_LOAD_PATH") ;;; (System/getProperty "java.class.path")
58 | (Regex. (str "\\" System.IO.Path/PathSeparator)))))) ;;; (Pattern/compile (Pattern/quote File/pathSeparator))))))
59 |
60 | (defn scan-files
61 | "Scans files to find those which have changed since the last time
62 | 'scan-files' was run; updates the dependency tracker with
63 | new/changed/deleted files.
64 |
65 | files is the collection of files to scan.
66 |
67 | Optional third argument is map of options:
68 |
69 | :platform Either clj (default) or cljs, both defined in
70 | clojure.tools.namespace.find, controls reader options for
71 | parsing files.
72 |
73 | :add-all? If true, assumes all extant files are modified regardless
74 | of filesystem timestamps."
75 | {:added "0.3.0"}
76 | ([tracker files] (scan-files tracker files nil))
77 | ([tracker files {:keys [platform add-all?]}]
78 | (let [deleted (seq (deleted-files tracker files))
79 | modified (if add-all?
80 | files
81 | (seq (modified-files tracker files)))]
82 | (if (or deleted modified)
83 | (update-files tracker deleted modified platform)
84 | tracker))))
85 |
86 | (defn scan-dirs
87 | "Scans directories for files which have changed since the last time
88 | 'scan-dirs' or 'scan-files' was run; updates the dependency tracker
89 | with new/changed/deleted files.
90 |
91 | dirs is the collection of directories to scan, defaults to all
92 | directories on Clojure's classpath.
93 |
94 | Optional third argument is map of options:
95 |
96 | :platform Either clj (default) or cljs, both defined in
97 | clojure.tools.namespace.find, controls file extensions
98 | and reader options.
99 |
100 | :add-all? If true, assumes all extant files are modified regardless
101 | of filesystem timestamps."
102 | {:added "0.3.0"}
103 | ([tracker] (scan-dirs tracker nil nil))
104 | ([tracker dirs] (scan-dirs tracker dirs nil))
105 | ([tracker dirs {:keys [platform add-all?] :as options}]
106 | (let [ds (or (seq dirs) (dirs-on-classpath))] ;;; (classpath-directories)
107 | (scan-files tracker (find-files ds platform) options))))
108 |
109 | (defn scan
110 | "DEPRECATED: replaced by scan-dirs.
111 |
112 | Scans directories for Clojure (.clj, .cljc) source files which have
113 | changed since the last time 'scan' was run; update the dependency
114 | tracker with new/changed/deleted files.
115 |
116 | If no dirs given, defaults to all directories on the classpath."
117 | {:added "0.2.0"
118 | :deprecated "0.3.0"}
119 | [tracker & dirs]
120 | (scan-dirs tracker dirs {:platform find/cljr})) ;;; find/clj -- is this correct?
121 |
122 | (defn scan-all
123 | "DEPRECATED: replaced by scan-dirs.
124 |
125 | Scans directories for all Clojure source files and updates the
126 | dependency tracker to reload files. If no dirs given, defaults to
127 | all directories on the classpath."
128 | {:added "0.2.0"
129 | :deprecated "0.3.0"}
130 | [tracker & dirs]
131 | (scan-dirs tracker dirs {:platform find/cljr :add-all? true})) ;;; find/clj -- is this correct?
132 |
133 | ;;; ADDED
134 |
135 | (defn- make-dir-info
136 | ^DirectoryInfo [x]
137 | (cond
138 | (instance? DirectoryInfo x) x
139 | (string? x) (DirectoryInfo. ^String x)
140 | :default (DirectoryInfo. (str x))))
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/parse_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.parse-test
2 | (:use [clojure.test :only (deftest is)]
3 | [clojure.tools.namespace.parse :only (deps-from-ns-decl
4 | read-ns-decl)]))
5 |
6 | (def ns-decl-prefix-list
7 | '(ns com.example.one
8 | (:require (com.example two
9 | [three :as three]
10 | [four :refer (a b)])
11 | (com.example.sub [five :as five]
12 | six))
13 | (:use (com.example seven
14 | [eight :as eight]
15 | (nine :only (c d))
16 | [ten]))))
17 |
18 | ;; Some people like to write prefix lists as vectors, not lists. The
19 | ;; use/require functions accept this form.
20 | (def ns-decl-prefix-list-as-vector
21 | '(ns com.example.one
22 | (:require [com.example
23 | two
24 | [three :as three]
25 | [four :refer (a b)]]
26 | [com.example.sub
27 | [five :as five]
28 | six
29 | [eleven :as-alias eleven]])
30 | (:use [com.example
31 | seven
32 | [eight :as eight]
33 | (nine :only (c d))
34 | [ten]])))
35 |
36 | (def ns-decl-prefix-list-clauses-as-vectors
37 | "Sometimes people even write the clauses inside ns as vectors, which
38 | clojure.core/ns allows. See TNS-21."
39 | '(ns com.example.one
40 | [:require [com.example
41 | two
42 | [three :as three]
43 | [four :refer (a b)]]
44 | [com.example.sub
45 | [five :as five]
46 | six]]
47 | [:use [com.example
48 | seven
49 | [eight :as eight]
50 | (nine :only (c d))
51 | [ten]]]))
52 |
53 | (def deps-from-prefix-list
54 | '#{com.example.two
55 | com.example.three
56 | com.example.four
57 | com.example.sub.five
58 | com.example.sub.six
59 | com.example.seven
60 | com.example.eight
61 | com.example.nine
62 | com.example.ten})
63 |
64 | (deftest t-prefix-list
65 | (is (= deps-from-prefix-list
66 | (deps-from-ns-decl ns-decl-prefix-list))))
67 |
68 | (deftest t-prefix-list-as-vector
69 | (is (= deps-from-prefix-list
70 | (deps-from-ns-decl ns-decl-prefix-list-as-vector))))
71 |
72 | (deftest t-prefix-list-clauses-as-vectors
73 | (is (= deps-from-prefix-list
74 | (deps-from-ns-decl ns-decl-prefix-list-clauses-as-vectors))))
75 |
76 | (deftest t-no-deps-returns-empty-set
77 | (is (= #{} (deps-from-ns-decl '(ns com.example.one)))))
78 |
79 | (def multiple-ns-decls
80 | '((ns one)
81 | (ns two (:require one))
82 | (ns three (:require [one :as o] [two :as t]))))
83 |
84 | (def multiple-ns-decls-string
85 | " (println \"Code before first ns\")
86 | (ns one)
87 | (println \"Some code\")
88 | (defn hello-world [] \"Hello, World!\")
89 | (ns two (:require one))
90 | (println \"Some more code\")
91 | (ns three (:require [one :as o] [two :as t]))")
92 |
93 | (deftest t-read-multiple-ns-decls
94 | (with-open [rdr (clojure.lang.PushbackTextReader. ;;; java.io.PushbackReader.
95 | (System.IO.StringReader. multiple-ns-decls-string))] ;;; java.io.StringReader.
96 | (is (= multiple-ns-decls
97 | (take-while identity (repeatedly #(read-ns-decl rdr)))))))
98 |
99 | (def ns-docstring-example
100 | "The example ns declaration used in the docstring of clojure.core/ns"
101 | '(ns foo.bar
102 | (:refer-clojure :exclude [ancestors printf])
103 | (:require (clojure.contrib sql combinatorics))
104 | (:use (my.lib this that))
105 | (:import (java.util Date Timer Random)
106 | (java.sql Connection Statement))))
107 |
108 | (def deps-from-ns-docstring-example
109 | '#{clojure.contrib.sql
110 | clojure.contrib.combinatorics
111 | my.lib.this
112 | my.lib.that})
113 |
114 | (deftest t-ns-docstring-example
115 | (is (= deps-from-ns-docstring-example
116 | (deps-from-ns-decl ns-docstring-example))))
117 |
118 | (def require-docstring-example
119 | "The example ns declaration used in the docstring of
120 | clojure.core/require"
121 | '(ns (:require (clojure zip [set :as s]))))
122 |
123 | (def deps-from-require-docstring-example
124 | '#{clojure.zip
125 | clojure.set})
126 |
127 | (deftest t-require-docstring-example
128 | (is (= deps-from-require-docstring-example
129 | (deps-from-ns-decl require-docstring-example))))
130 |
131 | (def multiple-clauses
132 | "Example showing more than one :require or :use clause in one ns
133 | declaration, which clojure.core/ns allows."
134 | '(ns foo.bar
135 | (:require com.example.one)
136 | (:import java.io.File)
137 | (:require (com.example two three))
138 | (:use (com.example [four :only [x]]))
139 | (:use (com.example (five :only [x])))))
140 |
141 | (def deps-from-multiple-clauses
142 | '#{com.example.one
143 | com.example.two
144 | com.example.three
145 | com.example.four
146 | com.example.five})
147 |
148 | (deftest t-deps-from-multiple-clauses
149 | (is (= deps-from-multiple-clauses
150 | (deps-from-ns-decl multiple-clauses))))
151 |
152 | (def clauses-without-keywords
153 | "Example of require/use clauses with symbols instead of keywords,
154 | which clojure.core/ns allows."
155 | '(ns foo.bar
156 | (require com.example.one)
157 | (import java.io.File)
158 | (use (com.example (prefixes (two :only [x])
159 | three)))))
160 |
161 | (def deps-from-clauses-without-keywords
162 | '#{com.example.one
163 | com.example.prefixes.two
164 | com.example.prefixes.three})
165 |
166 | (deftest t-clauses-without-keywords
167 | (is (= deps-from-clauses-without-keywords
168 | (deps-from-ns-decl clauses-without-keywords))))
169 |
170 | (def reader-conditionals-string
171 | "(ns com.examples.one
172 | (:require #?(:cljr clojure.string ;;; :clj
173 | :cljs goog.string)))")
174 |
175 | (defn str->ns-decl [^String s]
176 | (-> s
177 | System.IO.StringReader. ;;; java.io.StringReader.
178 | clojure.lang.PushbackTextReader. ;;; java.io.PushbackReader.
179 | read-ns-decl))
180 |
181 | (deftest t-reader-conditionals
182 | (when (resolve 'clojure.core/reader-conditional?)
183 | (let [actual (-> reader-conditionals-string
184 | str->ns-decl
185 | deps-from-ns-decl)]
186 | (is (= #{'clojure.string} actual)))))
187 |
188 | (def cljs-ns-with-npm-dependency
189 | "(ns com.examples.one
190 | (:require [\"foobar\"] [baz]))")
191 |
192 | (deftest cljs-string-dependency
193 | (let [actual (-> cljs-ns-with-npm-dependency
194 | str->ns-decl
195 | deps-from-ns-decl)]
196 | (is (= #{'baz} actual))))
197 |
198 | (def cljs-ns-with-require-macros
199 | "(ns com.examples.one
200 | (:require-macros [org.macros :refer [my-macro]]))")
201 |
202 | (deftest cljs-require-macros
203 | (let [actual (-> cljs-ns-with-require-macros
204 | str->ns-decl
205 | deps-from-ns-decl)]
206 | (is (= #{'org.macros} actual))))
--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
1 | # Change Log for tools.namespace
2 |
3 |
4 | ## 0.3.x series
5 |
6 | ### Version 0.3.0-alpha1 on 14-Aug-2015
7 |
8 | * **Partial ClojureScript Support** [TNS-35]
9 |
10 | * Platform-neutral namespaces were converted to conditional-read
11 | (`.cljc`) files: c.t.n.dependency, c.t.n.track, and
12 | c.t.n.parse. These namespaces can be used in ClojureScript
13 | programs.
14 |
15 | * Added support for finding, parsing, and analyzing
16 | ClojureScript source files (`.cljs` and `.cljc`) in
17 | c.t.n.file, c.t.n.parse, c.t.n.dir, and c.t.n.find with
18 | optional "platform" arguments. These namespaces still only
19 | **run** on the Clojure(JVM) platform.
20 |
21 | * Reloading and interactive features remain Clojure(JVM) only
22 | for now: c.t.n.move, c.t.n.reload, and c.t.n.repl
23 |
24 | * Uses [tools.reader] for platform-independent parsing and
25 | conditional-reader support.
26 |
27 | * Minimum Clojure version is 1.7.0
28 |
29 | * Breaking change: `c.t.n.parse/read-ns-decl` no longer returns
30 | `nil` on syntax errors. Instead, exceptions are allowed to
31 | propagate up from tools.reader. This change only affects code
32 | which calls `read-ns-decl` directly. c.t.n.file and c.t.n.find
33 | will catch and ignore reader exeptions when trying to read
34 | namespace declarations.
35 |
36 | * Enhancement [TNS-36]: Use java.classpath for better JVM classpath
37 | resolution
38 |
39 | * Possible breaking change: parse/read-ns-decl does not capture
40 | reader errors
41 |
42 | * Some definitions deprecated; see source code or Var metadata for
43 | details.
44 |
45 |
46 |
47 | ## 0.2.x series
48 |
49 | ### Version 0.2.11 on 19-Jun-2015
50 |
51 | * [TNS-34] Allow reader conditionals in parsed source files
52 |
53 | ### Version 0.2.10 on 26-Feb-2015
54 |
55 | * Widen existing functions to handle both clj and cljc files in
56 | advance of reader conditional support in Clojure 1.7.
57 |
58 | ### Version 0.2.9 on 31-Jan-2015
59 |
60 | * Fix [TNS-20]: Undefined 'unload' order after namespaces are first
61 | added to an new, empty tracker.
62 |
63 | * Improvement [TNS-21]: Support `ns` clauses which use vectors
64 | instead of lists for clauses, contrary to docs.
65 |
66 | * Improvement [TNS-32]: Support `ns` clauses which use symbols as
67 | clause heads instead of keywords, contrary to docs.
68 |
69 | ### Version 0.2.8 on 19-Dec-2014
70 |
71 | * Improvement [TNS-31]: Specific error message when `:after` symbol
72 | passed to `refresh` cannot be resolved.
73 |
74 | * Fix [TNS-26]: namespace alias recovery after failed reload did not
75 | work due to local binding shadowing global Var
76 |
77 | ### Version 0.2.7 on 19-Sept-2014
78 |
79 | * [Revert bad commit](https://github.com/clojure/tools.namespace/commit/27194f2edfe3f5f9e1343f993beca4b43f0bafe8)
80 | mistakenly included in 0.2.6 which could cause the tracker's
81 | 'unload' order to be incorrect. See discussion at [TNS-20].
82 |
83 | ### **BROKEN** Version 0.2.6 on 7-Sept-2014 **DO NOT USE**
84 |
85 | * `clojure.tools.namespace.parse/read-ns-decl` asserts that its
86 | argument is a PushbackReader, instead of silently returning nil
87 |
88 | * Fix [TNS-22]: broken `clojure.string/replace` with Windows path
89 | separator
90 |
91 | ### Version 0.2.5 on 15-Jul-2014
92 |
93 | * New `clojure.tools.namespace.repl/clear` empties the state of the
94 | REPL dependency tracker. This can help repair the dependency
95 | tracker after a failed load or a circular dependency error.
96 |
97 | * Enhancement [TNS-19]: `deps-from-ns-decl` should return an empty
98 | set instead of nil. This may be a breaking change for some but
99 | is consistent with the original docstring.
100 |
101 | * Enhancement [TNS-18]: Compute transitive dependencies in linear time.
102 |
103 | * Enhancement [TNS-17]: The `ns` form doesn't need to be the first
104 | top-level form in a file.
105 |
106 | * Fix [TNS-16]: Don't depend on specific hash ordering in tests.
107 | Exposed by new hash functions in Clojure 1.6.0.
108 |
109 | * Fix [TNS-15]: Handle spaces in classpath directories (old
110 | `clojure.tools.namespace`)
111 |
112 | * Fix [TNS-12]: Duplicate definition of `jar-file?`
113 |
114 | ### Version 0.2.4 on 19-Jul-2013
115 |
116 | * Fix [TNS-10]: Forbid circular dependency when a namespace depends
117 | on itself
118 |
119 | * Fix [TNS-9] and [TNS-11]: support other prefix-list forms
120 |
121 | * Fix [TNS-8]: In `move-ns`, do not modify files whose contents does
122 | not change
123 |
124 | ### Version 0.2.3 on 01-Apr-2013
125 |
126 | * New: Attempt recovery of aliases/refers in REPL after error
127 |
128 | ### Version 0.2.2 on 14-Dec-2012
129 |
130 | * New: Add `:after` option to `refresh`
131 |
132 | * New: Add `clojure.tools.namespace.move`
133 |
134 | * Fix [TNS-4], reflection warnings
135 |
136 | ### Version 0.2.1 on 26-Oct-2012
137 |
138 | * Fix: Restore deprecated 0.1.x APIs in `clojure.tools.namespace`
139 |
140 | * Fix [TNS-3], actually use `refresh-dirs`
141 |
142 | ### **BROKEN** Version 0.2.0 on 05-Oct-2012 **DO NOT USE**
143 |
144 | * Not recommended for use: this release introduced breaking API
145 | changes (renaming core namespaces and functions) without
146 | backwards-compatibility. Applications with dependencies on both
147 | the 0.2.x and 0.1.x APIs cannot use this version.
148 |
149 | * New dependency tracking & reloading features
150 |
151 | * Eliminate dependency on [java.classpath]
152 |
153 |
154 |
155 | ## 0.1.x series
156 |
157 | ### Version 0.1.3 on 24-Apr-2012
158 |
159 | * Fix [TNS-1] Workaround for Clojure 1.2 reader bug
160 |
161 | ### Version 0.1.2 on 10-Feb-2012
162 |
163 | * Fix: Eliminate reflection warnings
164 |
165 | ### Version 0.1.1 on 18-May-2011
166 |
167 | ### Version 0.1.0 on 24-Apr-2011
168 |
169 | * Source-compatible with clojure.contrib.find-namespaces in old
170 | clojure-contrib 1.2.0
171 |
172 | [TNS-1]: http://dev.clojure.org/jira/browse/TNS-1
173 | [TNS-3]: http://dev.clojure.org/jira/browse/TNS-3
174 | [TNS-4]: http://dev.clojure.org/jira/browse/TNS-4
175 | [TNS-8]: http://dev.clojure.org/jira/browse/TNS-8
176 | [TNS-9]: http://dev.clojure.org/jira/browse/TNS-9
177 | [TNS-10]: http://dev.clojure.org/jira/browse/TNS-10
178 | [TNS-11]: http://dev.clojure.org/jira/browse/TNS-11
179 | [TNS-12]: http://dev.clojure.org/jira/browse/TNS-12
180 | [TNS-15]: http://dev.clojure.org/jira/browse/TNS-15
181 | [TNS-16]: http://dev.clojure.org/jira/browse/TNS-16
182 | [TNS-17]: http://dev.clojure.org/jira/browse/TNS-17
183 | [TNS-18]: http://dev.clojure.org/jira/browse/TNS-18
184 | [TNS-19]: http://dev.clojure.org/jira/browse/TNS-19
185 | [TNS-20]: http://dev.clojure.org/jira/browse/TNS-20
186 | [TNS-21]: http://dev.clojure.org/jira/browse/TNS-21
187 | [TNS-22]: http://dev.clojure.org/jira/browse/TNS-22
188 | [TNS-23]: http://dev.clojure.org/jira/browse/TNS-23
189 | [TNS-24]: http://dev.clojure.org/jira/browse/TNS-24
190 | [TNS-25]: http://dev.clojure.org/jira/browse/TNS-25
191 | [TNS-26]: http://dev.clojure.org/jira/browse/TNS-26
192 | [TNS-27]: http://dev.clojure.org/jira/browse/TNS-27
193 | [TNS-28]: http://dev.clojure.org/jira/browse/TNS-28
194 | [TNS-29]: http://dev.clojure.org/jira/browse/TNS-29
195 | [TNS-30]: http://dev.clojure.org/jira/browse/TNS-30
196 | [TNS-31]: http://dev.clojure.org/jira/browse/TNS-31
197 | [TNS-32]: http://dev.clojure.org/jira/browse/TNS-32
198 | [TNS-33]: http://dev.clojure.org/jira/browse/TNS-33
199 | [TNS-34]: http://dev.clojure.org/jira/browse/TNS-34
200 | [TNS-35]: http://dev.clojure.org/jira/browse/TNS-35
201 | [TNS-36]: http://dev.clojure.org/jira/browse/TNS-36
202 | [java.classpath]: https://github.com/clojure/java.classpath
203 | [tools.reader]: https://github.com/clojure/tools.reader
204 | [JEP 122]: http://openjdk.java.net/jeps/122
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/repl.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns ^{:author "Stuart Sierra"
10 | :doc "REPL utilities for working with namespaces"}
11 | clojure.tools.namespace.repl
12 | (:require [clojure.tools.namespace.track :as track]
13 | [clojure.tools.namespace.dir :as dir]
14 | [clojure.tools.namespace.find :as find]
15 | [clojure.tools.namespace.reload :as reload]))
16 |
17 | (defonce refresh-tracker (track/tracker))
18 |
19 | (defonce refresh-dirs [])
20 |
21 | (defn- print-and-return [tracker]
22 | (if-let [e (::reload/error tracker)]
23 | (do (when (thread-bound? #'*e)
24 | (set! *e e))
25 | (prn :error-while-loading (::reload/error-ns tracker))
26 | e)
27 | :ok))
28 |
29 | (defn- print-pending-reloads [tracker]
30 | (prn :reloading (::track/load tracker)))
31 |
32 | (defn- load-disabled? [sym]
33 | (false? (::load (meta (find-ns sym)))))
34 |
35 | (defn- unload-disabled? [sym]
36 | (or (false? (::unload (meta (find-ns sym))))
37 | (load-disabled? sym)))
38 |
39 | (defn- remove-disabled [tracker]
40 | (-> tracker
41 | (update-in [::track/unload] #(remove unload-disabled? %))
42 | (update-in [::track/load] #(remove load-disabled? %))))
43 |
44 | (defn- referred
45 | "Given a Namespace object, returns a map of symbols describing the
46 | Vars it refers from other namespaces, in the following form:
47 |
48 | {other-namespace-name {symbol-in-other-ns symbol-in-this-ns}}"
49 | [ns]
50 | (reduce (fn [m [sym var]]
51 | (let [ns-name (ns-name (:ns (meta var)))
52 | var-name (:name (meta var))]
53 | (assoc-in m [ns-name var-name] sym)))
54 | {}
55 | (ns-refers ns)))
56 |
57 | (defn- aliased
58 | "Given a namespace object, returns a map of symbols describing its
59 | aliases, in the following form:
60 |
61 | {alias-symbol namespace-name}"
62 | [ns]
63 | (reduce (fn [m [alias n]] (assoc m alias (ns-name n)))
64 | {} (ns-aliases ns)))
65 |
66 | (defn- recover-ns
67 | "Given the maps returned by 'referred' and 'aliased', attempts to
68 | restore as many bindings as possible into the current namespace. Any
69 | bindings to namespaces or Vars which do not currently exist will be
70 | ignored."
71 | [refers aliases]
72 | (doseq [[ns-name symbol-map] refers]
73 | (when-let [ns (find-ns ns-name)]
74 | (doseq [[source-name target-name] symbol-map]
75 | (when (ns-resolve ns source-name)
76 | (if (= source-name target-name)
77 | (refer ns-name :only (list source-name))
78 | (refer ns-name :only () :rename {source-name target-name}))))))
79 | (doseq [[alias-sym ns-name] aliases]
80 | (when (find-ns ns-name)
81 | (alias alias-sym ns-name))))
82 |
83 | (defn scan
84 | "Scans directories for files which have changed since the last time
85 | 'scan' or 'refresh' was run; updates the dependency tracker
86 | with new/changed/deleted files.
87 |
88 | Optional argument is map of options:
89 |
90 | :platform Either clj (default) or cljs, both defined in
91 | clojure.tools.namespace.find, controls file extensions
92 | and reader options.
93 |
94 | :add-all? If true, assumes all extant files are modified regardless
95 | of filesystem timestamps.
96 |
97 | Returns map with keys:
98 |
99 | ::track/unload list of namespace symbols that will be unloaded
100 | ::track/load list of namespace symbols that will be loaded"
101 | ([]
102 | (scan nil))
103 | ([options]
104 | (alter-var-root #'refresh-tracker
105 | #(-> %
106 | (dir/scan-dirs refresh-dirs options)
107 | (remove-disabled)))))
108 |
109 | (defn refresh-scanned
110 | "Reloads namespaces in dependency order. Does not scan directories again,
111 | expected to be used after 'scan'.
112 |
113 | Returns :ok or an error; sets the latest exception to
114 | clojure.core/*e (if *e is thread-bound).
115 |
116 | The directories to be scanned are controlled by 'set-refresh-dirs';
117 | defaults to all directories on the Java classpath.
118 |
119 | Options are key-value pairs. Valid options are:
120 |
121 | :after Namespace-qualified symbol naming a zero-argument
122 | function to be invoked after a successful refresh. This
123 | symbol will be resolved *after* all namespaces have
124 | been reloaded."
125 | [& options]
126 | (let [{:keys [after]} options]
127 | (when after
128 | (assert (symbol? after) ":after value must be a symbol")
129 | (assert (namespace after)
130 | ":after value must be a namespace-qualified symbol"))
131 | (let [current-ns-name (ns-name *ns*)
132 | current-ns-refers (referred *ns*)
133 | current-ns-aliases (aliased *ns*)]
134 | (print-pending-reloads refresh-tracker)
135 | (alter-var-root #'refresh-tracker reload/track-reload)
136 | (in-ns current-ns-name)
137 | (let [result (print-and-return refresh-tracker)]
138 | (if (= :ok result)
139 | (if after
140 | (if-let [after (ns-resolve *ns* after)]
141 | (after)
142 | (throw (Exception.
143 | (str "Cannot resolve :after symbol " after))))
144 | result)
145 | ;; There was an error, recover as much as we can:
146 | (do (when-not (or (false? (::unload (meta *ns*)))
147 | (false? (::load (meta *ns*))))
148 | (recover-ns current-ns-refers current-ns-aliases))
149 | ;; Return the Exception to the REPL:
150 | result))))))
151 |
152 | (defn disable-unload!
153 | "Adds metadata to namespace (or *ns* if unspecified) telling
154 | 'refresh' not to unload it. The namespace may still be reloaded, it
155 | just won't be removed first.
156 |
157 | Warning: Aliases to reloaded namespaces will break."
158 | ([] (disable-unload! *ns*))
159 | ([namespace] (alter-meta! namespace assoc ::unload false)))
160 |
161 | (defn disable-reload!
162 | "Adds metadata to namespace (or *ns* if unspecified) telling
163 | 'refresh' not to load it. Implies disable-unload! also.
164 |
165 | Warning: Aliases to reloaded namespaces will break."
166 | ([] (disable-reload! *ns*))
167 | ([namespace] (alter-meta! namespace assoc ::load false)))
168 |
169 | (defn refresh
170 | "Scans source code directories for files which have changed (since
171 | the last time this function was run) and reloads them in dependency
172 | order. Returns :ok or an error; sets the latest exception to
173 | clojure.core/*e (if *e is thread-bound).
174 |
175 | The directories to be scanned are controlled by 'set-refresh-dirs';
176 | defaults to all directories on the Java classpath.
177 |
178 | Options are key-value pairs. Valid options are:
179 |
180 | :after Namespace-qualified symbol naming a zero-argument
181 | function to be invoked after a successful refresh. This
182 | symbol will be resolved *after* all namespaces have
183 | been reloaded."
184 | [& options]
185 | (let [{:keys [after]} options]
186 | (scan {:platform find/clj})
187 | (apply refresh-scanned options)))
188 |
189 | (defn refresh-all
190 | "Scans source code directories for all Clojure source files and
191 | reloads them in dependency order.
192 |
193 | The directories to be scanned are controlled by 'set-refresh-dirs';
194 | defaults to all directories on the Java classpath.
195 |
196 | Options are key-value pairs. Valid options are:
197 |
198 | :after Namespace-qualified symbol naming a zero-argument
199 | function to be invoked after a successful refresh. This
200 | symbol will be resolved *after* all namespaces have
201 | been reloaded."
202 | [& options]
203 | (let [{:keys [after]} options]
204 | (scan {:platform find/clj
205 | :add-all? true})
206 | (apply refresh-scanned options)))
207 |
208 | (defn set-refresh-dirs
209 | "Sets the directories which are scanned by 'refresh'. Supports the
210 | same types as clojure.java.io/file."
211 | [& dirs]
212 | (alter-var-root #'refresh-dirs (constantly dirs)))
213 |
214 | (defn clear
215 | "Clears all state from the namespace/file tracker. This may help
216 | repair the namespace tracker when it gets into an inconsistent
217 | state, without restarting the Clojure process. The next call to
218 | 'refresh' will reload all source files, but may not completely
219 | remove stale code from deleted files."
220 | []
221 | (alter-var-root #'refresh-tracker (constantly (track/tracker))))
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/namespace/dependency_test.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.namespace.dependency-test
2 | (:use clojure.test
3 | clojure.tools.namespace.dependency))
4 |
5 | ;; building a graph like:
6 | ;;
7 | ;; :a
8 | ;; / |
9 | ;; :b |
10 | ;; \ |
11 | ;; :c
12 | ;; |
13 | ;; :d
14 | ;;
15 | (def g1 (-> (graph)
16 | (depend :b :a) ; "B depends on A"
17 | (depend :c :b) ; "C depends on B"
18 | (depend :c :a) ; "C depends on A"
19 | (depend :d :c))) ; "D depends on C"
20 |
21 | ;; 'one 'five
22 | ;; | |
23 | ;; 'two |
24 | ;; / \ |
25 | ;; / \ |
26 | ;; / \ /
27 | ;; 'three 'four
28 | ;; | /
29 | ;; 'six /
30 | ;; | /
31 | ;; | /
32 | ;; | /
33 | ;; 'seven
34 | ;;
35 | (def g2 (-> (graph)
36 | (depend 'two 'one)
37 | (depend 'three 'two)
38 | (depend 'four 'two)
39 | (depend 'four 'five)
40 | (depend 'six 'three)
41 | (depend 'seven 'six)
42 | (depend 'seven 'four)))
43 |
44 | ;; :level0
45 | ;; / | | \
46 | ;; ----- | | -----
47 | ;; / | | \
48 | ;; :level1a :level1b :level1c :level1d
49 | ;; \ | | /
50 | ;; ----- | | -----
51 | ;; \ | | /
52 | ;; :level2
53 | ;; / | | \
54 | ;; ----- | | -----
55 | ;; / | | \
56 | ;; :level3a :level3b :level3c :level3d
57 | ;; \ | | /
58 | ;; ----- | | -----
59 | ;; \ | | /
60 | ;; :level4
61 | ;;
62 | ;; ... and so on in a repeating pattern like that, up to :level26
63 |
64 | (def g3 (-> (graph)
65 | (depend :level1a :level0)
66 | (depend :level1b :level0)
67 | (depend :level1c :level0)
68 | (depend :level1d :level0)
69 | (depend :level2 :level1a)
70 | (depend :level2 :level1b)
71 | (depend :level2 :level1c)
72 | (depend :level2 :level1d)
73 |
74 | (depend :level3a :level2)
75 | (depend :level3b :level2)
76 | (depend :level3c :level2)
77 | (depend :level3d :level2)
78 | (depend :level4 :level3a)
79 | (depend :level4 :level3b)
80 | (depend :level4 :level3c)
81 | (depend :level4 :level3d)
82 |
83 | (depend :level5a :level4)
84 | (depend :level5b :level4)
85 | (depend :level5c :level4)
86 | (depend :level5d :level4)
87 | (depend :level6 :level5a)
88 | (depend :level6 :level5b)
89 | (depend :level6 :level5c)
90 | (depend :level6 :level5d)
91 |
92 | (depend :level7a :level6)
93 | (depend :level7b :level6)
94 | (depend :level7c :level6)
95 | (depend :level7d :level6)
96 | (depend :level8 :level7a)
97 | (depend :level8 :level7b)
98 | (depend :level8 :level7c)
99 | (depend :level8 :level7d)
100 |
101 | (depend :level9a :level8)
102 | (depend :level9b :level8)
103 | (depend :level9c :level8)
104 | (depend :level9d :level8)
105 | (depend :level10 :level9a)
106 | (depend :level10 :level9b)
107 | (depend :level10 :level9c)
108 | (depend :level10 :level9d)
109 |
110 | (depend :level11a :level10)
111 | (depend :level11b :level10)
112 | (depend :level11c :level10)
113 | (depend :level11d :level10)
114 | (depend :level12 :level11a)
115 | (depend :level12 :level11b)
116 | (depend :level12 :level11c)
117 | (depend :level12 :level11d)
118 |
119 | (depend :level13a :level12)
120 | (depend :level13b :level12)
121 | (depend :level13c :level12)
122 | (depend :level13d :level12)
123 | (depend :level14 :level13a)
124 | (depend :level14 :level13b)
125 | (depend :level14 :level13c)
126 | (depend :level14 :level13d)
127 |
128 | (depend :level15a :level14)
129 | (depend :level15b :level14)
130 | (depend :level15c :level14)
131 | (depend :level15d :level14)
132 | (depend :level16 :level15a)
133 | (depend :level16 :level15b)
134 | (depend :level16 :level15c)
135 | (depend :level16 :level15d)
136 |
137 | (depend :level17a :level16)
138 | (depend :level17b :level16)
139 | (depend :level17c :level16)
140 | (depend :level17d :level16)
141 | (depend :level18 :level17a)
142 | (depend :level18 :level17b)
143 | (depend :level18 :level17c)
144 | (depend :level18 :level17d)
145 |
146 | (depend :level19a :level18)
147 | (depend :level19b :level18)
148 | (depend :level19c :level18)
149 | (depend :level19d :level18)
150 | (depend :level20 :level19a)
151 | (depend :level20 :level19b)
152 | (depend :level20 :level19c)
153 | (depend :level20 :level19d)
154 |
155 | (depend :level21a :level20)
156 | (depend :level21b :level20)
157 | (depend :level21c :level20)
158 | (depend :level21d :level20)
159 | (depend :level22 :level21a)
160 | (depend :level22 :level21b)
161 | (depend :level22 :level21c)
162 | (depend :level22 :level21d)
163 |
164 | (depend :level23a :level22)
165 | (depend :level23b :level22)
166 | (depend :level23c :level22)
167 | (depend :level23d :level22)
168 | (depend :level24 :level23a)
169 | (depend :level24 :level23b)
170 | (depend :level24 :level23c)
171 | (depend :level24 :level23d)
172 |
173 | (depend :level25a :level24)
174 | (depend :level25b :level24)
175 | (depend :level25c :level24)
176 | (depend :level25d :level24)
177 | (depend :level26 :level25a)
178 | (depend :level26 :level25b)
179 | (depend :level26 :level25c)
180 | (depend :level26 :level25d)))
181 |
182 | (deftest t-transitive-dependencies
183 | (is (= #{:a :c :b}
184 | (transitive-dependencies g1 :d)))
185 | (is (= '#{two four six one five three}
186 | (transitive-dependencies g2 'seven))))
187 |
188 | (deftest t-transitive-dependencies-deep
189 | (is (= #{:level0
190 | :level1a :level1b :level1c :level1d
191 | :level2
192 | :level3a :level3b :level3c :level3d
193 | :level4
194 | :level5a :level5b :level5c :level5d
195 | :level6
196 | :level7a :level7b :level7c :level7d
197 | :level8
198 | :level9a :level9b :level9c :level9d
199 | :level10
200 | :level11a :level11b :level11c :level11d
201 | :level12
202 | :level13a :level13b :level13c :level13d
203 | :level14
204 | :level15a :level15b :level15c :level15d
205 | :level16
206 | :level17a :level17b :level17c :level17d
207 | :level18
208 | :level19a :level19b :level19c :level19d
209 | :level20
210 | :level21a :level21b :level21c :level21d
211 | :level22
212 | :level23a :level23b :level23c :level23d}
213 | (transitive-dependencies g3 :level24)))
214 | (is (= #{:level0
215 | :level1a :level1b :level1c :level1d
216 | :level2
217 | :level3a :level3b :level3c :level3d
218 | :level4
219 | :level5a :level5b :level5c :level5d
220 | :level6
221 | :level7a :level7b :level7c :level7d
222 | :level8
223 | :level9a :level9b :level9c :level9d
224 | :level10
225 | :level11a :level11b :level11c :level11d
226 | :level12
227 | :level13a :level13b :level13c :level13d
228 | :level14
229 | :level15a :level15b :level15c :level15d
230 | :level16
231 | :level17a :level17b :level17c :level17d
232 | :level18
233 | :level19a :level19b :level19c :level19d
234 | :level20
235 | :level21a :level21b :level21c :level21d
236 | :level22
237 | :level23a :level23b :level23c :level23d
238 | :level24
239 | :level25a :level25b :level25c :level25d}
240 | (transitive-dependencies g3 :level26))))
241 |
242 |
243 | (deftest t-transitive-dependents
244 | (is (= '#{four seven}
245 | (transitive-dependents g2 'five)))
246 | (is (= '#{four seven six three}
247 | (transitive-dependents g2 'two))))
248 |
249 | (defn- before?
250 | "True if x comes before y in an ordered collection."
251 | [coll x y]
252 | (loop [[item & more] (seq coll)]
253 | (cond (nil? item) true ; end of the seq
254 | (= x item) true ; x comes first
255 | (= y item) false
256 | :else (recur more))))
257 |
258 | (deftest t-before
259 | (is (true? (before? [:a :b :c :d] :a :b)))
260 | (is (true? (before? [:a :b :c :d] :b :c)))
261 | (is (false? (before? [:a :b :c :d] :d :c)))
262 | (is (false? (before? [:a :b :c :d] :c :a))))
263 |
264 | (deftest t-topo-comparator-1
265 | (let [sorted (sort (topo-comparator g1) [:d :a :b :foo])]
266 | (are [x y] (before? sorted x y)
267 | :a :b
268 | :a :d
269 | :a :foo
270 | :b :d
271 | :b :foo
272 | :d :foo)))
273 |
274 | (deftest t-topo-comparator-2
275 | (let [sorted (sort (topo-comparator g2) '[three seven nine eight five])]
276 | (are [x y] (before? sorted x y)
277 | 'three 'seven
278 | 'three 'eight
279 | 'three 'nine
280 | 'five 'eight
281 | 'five 'nine
282 | 'seven 'eight
283 | 'seven 'nine)))
284 |
285 | (deftest t-topo-sort
286 | (let [sorted (topo-sort g2)]
287 | (are [x y] (before? sorted x y)
288 | 'one 'two
289 | 'one 'three
290 | 'one 'four
291 | 'one 'six
292 | 'one 'seven
293 | 'two 'three
294 | 'two 'four
295 | 'two 'six
296 | 'two 'seven
297 | 'three 'six
298 | 'three 'seven
299 | 'four 'seven
300 | 'five 'four
301 | 'five 'seven
302 | 'six 'seven)))
303 |
304 | (deftest t-no-cycles
305 | (is (thrown? Exception
306 | (-> (graph)
307 | (depend :a :b)
308 | (depend :b :c)
309 | (depend :c :a)))))
310 |
311 | (deftest t-no-self-cycles
312 | (is (thrown? Exception
313 | (-> (graph)
314 | (depend :a :b)
315 | (depend :a :a)))))
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/namespace/find.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Stuart Sierra, 2012. All rights reserved. The use and
2 | ;; distribution terms for this software are covered by the Eclipse
3 | ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
4 | ;; which can be found in the file epl-v10.html at the root of this
5 | ;; distribution. By using this software in any fashion, you are
6 | ;; agreeing to be bound by the terms of this license. You must not
7 | ;; remove this notice, or any other, from this software.
8 |
9 | (ns
10 | ^{:author "Stuart Sierra, modified for ClojureCLR by David Miller",
11 | :doc "Search for namespace declarations in directories and JAR files."}
12 | clojure.tools.namespace.find
13 | (:require ;;; Doesn't exist for us: [clojure.java.classpath :as classpath]
14 | [clojure.clr.io :as io] ;;; clojure.java.io
15 | [clojure.set :as set]
16 | [clojure.tools.namespace.file :as file]
17 | [clojure.tools.namespace.parse :as parse])
18 | (:import (System.IO TextReader ;;; (java.io File FileReader BufferedReader PushbackReader
19 | FileInfo DirectoryInfo FileSystemInfo) ;;; InputStreamReader)
20 | (clojure.lang PushbackTextReader))) ;;; (java.util.jar JarFile JarEntry)))
21 |
22 | (set! *warn-on-reflection* true)
23 |
24 | (def ^{:added "0.3.0"}
25 | clj
26 | "Platform definition of file extensions and reader options for
27 | Clojure (.clj and .cljc) source files."
28 | {:read-opts parse/clj-read-opts
29 | :extensions file/clojure-extensions})
30 |
31 | (def ^{:added "0.3.0"}
32 | cljs
33 | "Platform definition of file extensions and reader options for
34 | ClojureScript (.cljs and .cljc) source files."
35 | {:read-opts parse/cljs-read-opts
36 | :extensions file/clojurescript-extensions})
37 |
38 | (def ^{:added "0.3.0"}
39 | cljr
40 | "Platform definition of file extensions and reader options for
41 | ClojureCLR (.cljr and .cljc) source files."
42 | {:read-opts parse/cljr-read-opts
43 | :extensions file/clojure-clr-extensions})
44 |
45 | (defmacro ^:private ignore-reader-exception
46 | "If body throws an exception caused by a syntax error (from
47 | tools.reader), returns nil. Rethrows other exceptions."
48 | [& body]
49 | `(try ~@body
50 | (catch Exception e#
51 | (if (= :reader-exception (:type (ex-data e#)))
52 | nil
53 | (throw e#)))))
54 |
55 | ;;; Finding namespaces in a directory tree
56 |
57 | (defn- sort-files-breadth-first
58 | [files]
59 | (sort-by #(.FullName ^FileSystemInfo %) files)) ;#(.getAbsolutePath ^File %)
60 |
61 | (defn find-sources-in-dir
62 | "Searches recursively under dir for source files. Returns a sequence
63 | of File objects, in breadth-first sort order.
64 |
65 | Optional second argument is either clj (default) or cljs, both
66 | defined in clojure.tools.namespace.find."
67 | {:added "0.3.0"}
68 | ([dir]
69 | (find-sources-in-dir dir nil))
70 | ([^DirectoryInfo dir platform] ;;; ^File
71 | (let [{:keys [extensions]} (or platform clj)]
72 | (->> (file-seq dir)
73 | (filter #(file/file-with-extension? % extensions))
74 | sort-files-breadth-first))))
75 |
76 | (defn find-clojure-sources-in-dir
77 | "DEPRECATED: replaced by find-sources-in-dir
78 |
79 | Searches recursively under dir for Clojure source files (.clj, .cljc).
80 | Returns a sequence of File objects, in breadth-first sort order." ;;; SystemFileInfo
81 | {:added "0.2.0"
82 | :deprecated "0.3.0"}
83 | [^FileSystemInfo dir] ;;; ^File
84 | (find-sources-in-dir dir clj))
85 |
86 | (defn find-ns-decls-in-dir
87 | "Searches dir recursively for (ns ...) declarations in Clojure
88 | source files; returns the unevaluated ns declarations.
89 | The symbol name in the returned ns declaration will contain metadata
90 | for the corresponding directory and located file within at keys
91 | :dir and :file.
92 |
93 | Optional second argument platform is either clj (default) or cljs,
94 | both defined in clojure.tools.namespace.find."
95 | {:added "0.2.0"}
96 | ([dir] (find-ns-decls-in-dir dir nil))
97 | ([dir platform]
98 | (keep #(ignore-reader-exception
99 | (let [[_ nom & more :as decl] (file/read-file-ns-decl % (:read-opts platform))]
100 | (when (and decl nom (symbol? nom))
101 | (list* 'ns (with-meta nom
102 | {:dir (.Name ^DirectoryInfo dir) :file (.Name ^FileInfo %)}) ;; .getName ^java.io.File x 2
103 | more))))
104 | (find-sources-in-dir dir platform))))
105 |
106 | (defn find-namespaces-in-dir
107 | "Searches dir recursively for (ns ...) declarations in Clojure
108 | source files; returns the symbol names of the declared namespaces.
109 |
110 | Optional second argument platform is either clj (default) or cljs,
111 | both defined in clojure.tools.namespace.find."
112 | {:added "0.3.0"}
113 | ([dir] (find-namespaces-in-dir dir nil))
114 | ([dir platform]
115 | (map parse/name-from-ns-decl (find-ns-decls-in-dir dir platform))))
116 |
117 | ;;; Finding namespaces in JAR files
118 |
119 | (defn- ends-with-extension
120 | [^String filename extensions]
121 | (some #(.EndsWith filename ^String %) extensions)) ;;; .endsWith, add type hint
122 |
123 | (defn sources-in-jar
124 | "Returns a sequence of source file names found in the JAR file.
125 |
126 | Optional second argument platform is either clj (default) or cljs,
127 | both defined in clojure.tools.namespace.find."
128 | {:added "0.3.0"}
129 | ([jar-file]
130 | (sources-in-jar jar-file nil))
131 | ([jar-file platform] ;;; [^JarFile jar-file platform]
132 | nil)) ;;; (let [{:keys [extensions]} (or platform clj)]
133 | ;;; (filter #(ends-with-extension % extensions)
134 | ;;; (classpath/filenames-in-jar jar-file)))
135 |
136 | (defn clojure-sources-in-jar
137 | "DEPRECATED: replaced by sources-in-jar
138 |
139 | Returns a sequence of filenames ending in .clj or .cljc found in the
140 | JAR file."
141 | {:added "0.2.0"
142 | :deprecated "0.3.0"}
143 | [jar-file]
144 | (sources-in-jar jar-file clj))
145 |
146 | (defn read-ns-decl-from-jarfile-entry
147 | "Attempts to read a (ns ...) declaration from the named entry in the
148 | JAR file, and returns the unevaluated form. Returns nil if read
149 | fails due to invalid syntax or if a ns declaration cannot be found.
150 | The symbol name in the returned ns declaration will contain metadata
151 | for the corresponding jar filename and located file within at keys
152 | :jar and :file.
153 |
154 | Optional third argument platform is either clj (default) or cljs,
155 | both defined in clojure.tools.namespace.find."
156 | ([jarfile entry-name]
157 | (read-ns-decl-from-jarfile-entry jarfile entry-name nil))
158 | ([jarfile ^String entry-name platorm] ;;; [^JarFile jarfile ^String entry-name platform]
159 | nil)) ;;; (let [{:keys [read-opts]} (or platform clj)]
160 | ;;; (with-open [rdr (PushbackReader.
161 | ;;; (io/reader
162 | ;;; (.getInputStream jarfile (.getEntry jarfile entry-name))))]
163 | ;;; (ignore-reader-exception
164 | ;;; (let [[_ nom & more :as decl] (parse/read-ns-decl rdr read-opts)]
165 | ;;; (when (and decl nom (symbol? nom))
166 | ;;; (list* 'ns (with-meta nom
167 | ;;; {:jar (.getName ^JarFile jarfile) :file entry-name})
168 | ;;; more))))))))
169 | (defn find-ns-decls-in-jarfile
170 | "Searches the JAR file for source files containing (ns ...)
171 | declarations; returns the unevaluated ns declarations.
172 |
173 | Optional second argument platform is either clj (default) or cljs,
174 | both defined in clojure.tools.namespace.find."
175 | ([jarfile]
176 | (find-ns-decls-in-jarfile jarfile nil))
177 | ([jarfile platform] ;;; ^JarFile
178 | (keep #(read-ns-decl-from-jarfile-entry jarfile % platform)
179 | (sources-in-jar jarfile platform))))
180 |
181 | (defn find-namespaces-in-jarfile
182 | "Searches the JAR file for platform source files containing (ns ...)
183 | declarations. Returns a sequence of the symbol names of the
184 | declared namespaces.
185 |
186 | Optional second argument platform is either clj (default) or cljs,
187 | both defined in clojure.tools.namespace.find."
188 | ([jarfile]
189 | (find-namespaces-in-jarfile jarfile nil))
190 | ([jarfile platform] ;;; ^JarFile
191 | (map parse/name-from-ns-decl (find-ns-decls-in-jarfile jarfile platform))))
192 |
193 |
194 | ;;; Finding namespaces
195 |
196 | (defn find-ns-decls
197 | "Searches a sequence of java.io.File objects (both directories and
198 | JAR files) for platform source files containing (ns...)
199 | declarations. Returns a sequence of the unevaluated ns declaration
200 | forms. Use with clojure.java.classpath to search Clojure's
201 | classpath.
202 |
203 | Optional second argument platform is either clj (default) or cljs,
204 | both defined in clojure.tools.namespace.find."
205 | ([files]
206 | (find-ns-decls files nil))
207 | ([files platform]
208 | (concat
209 | (mapcat #(find-ns-decls-in-dir % platform)
210 | (filter file/is-directory? files)) ;;; #(.isDirectory ^File %)
211 | ;;;(mapcat #(find-ns-decls-in-jarfile % platform)
212 | ;;; (map #(JarFile. (io/file %))
213 | ))) ;;; (filter classpath/jar-file? files)))
214 |
215 | (defn find-namespaces
216 | "Searches a sequence of java.io.File objects (both directories and
217 | JAR files) for platform source files containing (ns...)
218 | declarations. Returns a sequence of the symbol names of the declared
219 | namespaces. Use with clojure.java.classpath to search Clojure's
220 | classpath.
221 |
222 | Optional second argument platform is either clj (default) or cljs,
223 | both defined in clojure.tools.namespace.find."
224 | ([files]
225 | (find-namespaces files nil))
226 | ([files platform]
227 | (map parse/name-from-ns-decl (find-ns-decls files platform))))
--------------------------------------------------------------------------------
/epl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Eclipse Public License - Version 1.0
8 |
25 |
26 |
27 |
28 |
29 |
30 | Eclipse Public License - v 1.0
31 |
32 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
33 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
34 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
35 | AGREEMENT.
36 |
37 | 1. DEFINITIONS
38 |
39 | "Contribution" means:
40 |
41 | a) in the case of the initial Contributor, the initial
42 | code and documentation distributed under this Agreement, and
43 | b) in the case of each subsequent Contributor:
44 | i) changes to the Program, and
45 | ii) additions to the Program;
46 | where such changes and/or additions to the Program
47 | originate from and are distributed by that particular Contributor. A
48 | Contribution 'originates' from a Contributor if it was added to the
49 | Program by such Contributor itself or anyone acting on such
50 | Contributor's behalf. Contributions do not include additions to the
51 | Program which: (i) are separate modules of software distributed in
52 | conjunction with the Program under their own license agreement, and (ii)
53 | are not derivative works of the Program.
54 |
55 | "Contributor" means any person or entity that distributes
56 | the Program.
57 |
58 | "Licensed Patents" mean patent claims licensable by a
59 | Contributor which are necessarily infringed by the use or sale of its
60 | Contribution alone or when combined with the Program.
61 |
62 | "Program" means the Contributions distributed in accordance
63 | with this Agreement.
64 |
65 | "Recipient" means anyone who receives the Program under
66 | this Agreement, including all Contributors.
67 |
68 | 2. GRANT OF RIGHTS
69 |
70 | a) Subject to the terms of this Agreement, each
71 | Contributor hereby grants Recipient a non-exclusive, worldwide,
72 | royalty-free copyright license to reproduce, prepare derivative works
73 | of, publicly display, publicly perform, distribute and sublicense the
74 | Contribution of such Contributor, if any, and such derivative works, in
75 | source code and object code form.
76 |
77 | b) Subject to the terms of this Agreement, each
78 | Contributor hereby grants Recipient a non-exclusive, worldwide,
79 | royalty-free patent license under Licensed Patents to make, use, sell,
80 | offer to sell, import and otherwise transfer the Contribution of such
81 | Contributor, if any, in source code and object code form. This patent
82 | license shall apply to the combination of the Contribution and the
83 | Program if, at the time the Contribution is added by the Contributor,
84 | such addition of the Contribution causes such combination to be covered
85 | by the Licensed Patents. The patent license shall not apply to any other
86 | combinations which include the Contribution. No hardware per se is
87 | licensed hereunder.
88 |
89 | c) Recipient understands that although each Contributor
90 | grants the licenses to its Contributions set forth herein, no assurances
91 | are provided by any Contributor that the Program does not infringe the
92 | patent or other intellectual property rights of any other entity. Each
93 | Contributor disclaims any liability to Recipient for claims brought by
94 | any other entity based on infringement of intellectual property rights
95 | or otherwise. As a condition to exercising the rights and licenses
96 | granted hereunder, each Recipient hereby assumes sole responsibility to
97 | secure any other intellectual property rights needed, if any. For
98 | example, if a third party patent license is required to allow Recipient
99 | to distribute the Program, it is Recipient's responsibility to acquire
100 | that license before distributing the Program.
101 |
102 | d) Each Contributor represents that to its knowledge it
103 | has sufficient copyright rights in its Contribution, if any, to grant
104 | the copyright license set forth in this Agreement.
105 |
106 | 3. REQUIREMENTS
107 |
108 | A Contributor may choose to distribute the Program in object code
109 | form under its own license agreement, provided that:
110 |
111 | a) it complies with the terms and conditions of this
112 | Agreement; and
113 |
114 | b) its license agreement:
115 |
116 | i) effectively disclaims on behalf of all Contributors
117 | all warranties and conditions, express and implied, including warranties
118 | or conditions of title and non-infringement, and implied warranties or
119 | conditions of merchantability and fitness for a particular purpose;
120 |
121 | ii) effectively excludes on behalf of all Contributors
122 | all liability for damages, including direct, indirect, special,
123 | incidental and consequential damages, such as lost profits;
124 |
125 | iii) states that any provisions which differ from this
126 | Agreement are offered by that Contributor alone and not by any other
127 | party; and
128 |
129 | iv) states that source code for the Program is available
130 | from such Contributor, and informs licensees how to obtain it in a
131 | reasonable manner on or through a medium customarily used for software
132 | exchange.
133 |
134 | When the Program is made available in source code form:
135 |
136 | a) it must be made available under this Agreement; and
137 |
138 | b) a copy of this Agreement must be included with each
139 | copy of the Program.
140 |
141 | Contributors may not remove or alter any copyright notices contained
142 | within the Program.
143 |
144 | Each Contributor must identify itself as the originator of its
145 | Contribution, if any, in a manner that reasonably allows subsequent
146 | Recipients to identify the originator of the Contribution.
147 |
148 | 4. COMMERCIAL DISTRIBUTION
149 |
150 | Commercial distributors of software may accept certain
151 | responsibilities with respect to end users, business partners and the
152 | like. While this license is intended to facilitate the commercial use of
153 | the Program, the Contributor who includes the Program in a commercial
154 | product offering should do so in a manner which does not create
155 | potential liability for other Contributors. Therefore, if a Contributor
156 | includes the Program in a commercial product offering, such Contributor
157 | ("Commercial Contributor") hereby agrees to defend and
158 | indemnify every other Contributor ("Indemnified Contributor")
159 | against any losses, damages and costs (collectively "Losses")
160 | arising from claims, lawsuits and other legal actions brought by a third
161 | party against the Indemnified Contributor to the extent caused by the
162 | acts or omissions of such Commercial Contributor in connection with its
163 | distribution of the Program in a commercial product offering. The
164 | obligations in this section do not apply to any claims or Losses
165 | relating to any actual or alleged intellectual property infringement. In
166 | order to qualify, an Indemnified Contributor must: a) promptly notify
167 | the Commercial Contributor in writing of such claim, and b) allow the
168 | Commercial Contributor to control, and cooperate with the Commercial
169 | Contributor in, the defense and any related settlement negotiations. The
170 | Indemnified Contributor may participate in any such claim at its own
171 | expense.
172 |
173 | For example, a Contributor might include the Program in a commercial
174 | product offering, Product X. That Contributor is then a Commercial
175 | Contributor. If that Commercial Contributor then makes performance
176 | claims, or offers warranties related to Product X, those performance
177 | claims and warranties are such Commercial Contributor's responsibility
178 | alone. Under this section, the Commercial Contributor would have to
179 | defend claims against the other Contributors related to those
180 | performance claims and warranties, and if a court requires any other
181 | Contributor to pay any damages as a result, the Commercial Contributor
182 | must pay those damages.
183 |
184 | 5. NO WARRANTY
185 |
186 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
187 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
188 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
189 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
190 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
191 | responsible for determining the appropriateness of using and
192 | distributing the Program and assumes all risks associated with its
193 | exercise of rights under this Agreement , including but not limited to
194 | the risks and costs of program errors, compliance with applicable laws,
195 | damage to or loss of data, programs or equipment, and unavailability or
196 | interruption of operations.
197 |
198 | 6. DISCLAIMER OF LIABILITY
199 |
200 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
201 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
202 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
203 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
204 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
205 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
206 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
207 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
208 |
209 | 7. GENERAL
210 |
211 | If any provision of this Agreement is invalid or unenforceable under
212 | applicable law, it shall not affect the validity or enforceability of
213 | the remainder of the terms of this Agreement, and without further action
214 | by the parties hereto, such provision shall be reformed to the minimum
215 | extent necessary to make such provision valid and enforceable.
216 |
217 | If Recipient institutes patent litigation against any entity
218 | (including a cross-claim or counterclaim in a lawsuit) alleging that the
219 | Program itself (excluding combinations of the Program with other
220 | software or hardware) infringes such Recipient's patent(s), then such
221 | Recipient's rights granted under Section 2(b) shall terminate as of the
222 | date such litigation is filed.
223 |
224 | All Recipient's rights under this Agreement shall terminate if it
225 | fails to comply with any of the material terms or conditions of this
226 | Agreement and does not cure such failure in a reasonable period of time
227 | after becoming aware of such noncompliance. If all Recipient's rights
228 | under this Agreement terminate, Recipient agrees to cease use and
229 | distribution of the Program as soon as reasonably practicable. However,
230 | Recipient's obligations under this Agreement and any licenses granted by
231 | Recipient relating to the Program shall continue and survive.
232 |
233 | Everyone is permitted to copy and distribute copies of this
234 | Agreement, but in order to avoid inconsistency the Agreement is
235 | copyrighted and may only be modified in the following manner. The
236 | Agreement Steward reserves the right to publish new versions (including
237 | revisions) of this Agreement from time to time. No one other than the
238 | Agreement Steward has the right to modify this Agreement. The Eclipse
239 | Foundation is the initial Agreement Steward. The Eclipse Foundation may
240 | assign the responsibility to serve as the Agreement Steward to a
241 | suitable separate entity. Each new version of the Agreement will be
242 | given a distinguishing version number. The Program (including
243 | Contributions) may always be distributed subject to the version of the
244 | Agreement under which it was received. In addition, after a new version
245 | of the Agreement is published, Contributor may elect to distribute the
246 | Program (including its Contributions) under the new version. Except as
247 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
248 | rights or licenses to the intellectual property of any Contributor under
249 | this Agreement, whether expressly, by implication, estoppel or
250 | otherwise. All rights in the Program not expressly granted under this
251 | Agreement are reserved.
252 |
253 | This Agreement is governed by the laws of the State of New York and
254 | the intellectual property laws of the United States of America. No party
255 | to this Agreement will bring a legal action under this Agreement more
256 | than one year after the cause of action arose. Each party waives its
257 | rights to a jury trial in any resulting litigation.
258 |
259 |
260 |
261 |
--------------------------------------------------------------------------------