aboutsummaryrefslogtreecommitdiff
path: root/lux-lein/src/leiningen
diff options
context:
space:
mode:
authorEduardo Julian2016-12-01 11:00:44 -0400
committerEduardo Julian2016-12-01 11:00:44 -0400
commit7f66c54f4c9753b94dbf46ec50b8b16549daf324 (patch)
tree1b5b896cfba870a66a99a03315b09df842eb5737 /lux-lein/src/leiningen
parent9c30546af022f8fe36b73e7e93414257ff28ee75 (diff)
- Collected the Lux compiler's repo, the Standard Library's, the Leiningen plugin's and the Emacs mode's into a big monorepo, to keep development unified.
Diffstat (limited to 'lux-lein/src/leiningen')
-rw-r--r--lux-lein/src/leiningen/luxc.clj27
-rw-r--r--lux-lein/src/leiningen/luxc/compiler.clj19
-rw-r--r--lux-lein/src/leiningen/luxc/packager.clj212
-rw-r--r--lux-lein/src/leiningen/luxc/repl.clj35
-rw-r--r--lux-lein/src/leiningen/luxc/test.clj27
-rw-r--r--lux-lein/src/leiningen/luxc/utils.clj97
6 files changed, 417 insertions, 0 deletions
diff --git a/lux-lein/src/leiningen/luxc.clj b/lux-lein/src/leiningen/luxc.clj
new file mode 100644
index 000000000..b04e4997a
--- /dev/null
+++ b/lux-lein/src/leiningen/luxc.clj
@@ -0,0 +1,27 @@
+;; Copyright (c) Eduardo Julian. All rights reserved.
+;; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+;; If a copy of the MPL was not distributed with this file,
+;; You can obtain one at http://mozilla.org/MPL/2.0/.
+
+(ns leiningen.luxc
+ (:require [leiningen.pom :as pom]
+ [leiningen.core.classpath :as classpath]
+ (leiningen.luxc [compiler :as &compiler]
+ [test :as &test]
+ [repl :as &repl])))
+
+;; [Exports]
+(defn luxc [project & args]
+ (case (first args)
+ "compile"
+ (&compiler/compile project)
+
+ "test"
+ (&test/test project)
+
+ "repl"
+ (&repl/repl project)
+
+ ;; default...
+ (println "Commands available: compile, test, repl"))
+ )
diff --git a/lux-lein/src/leiningen/luxc/compiler.clj b/lux-lein/src/leiningen/luxc/compiler.clj
new file mode 100644
index 000000000..18eef63a0
--- /dev/null
+++ b/lux-lein/src/leiningen/luxc/compiler.clj
@@ -0,0 +1,19 @@
+;; Copyright (c) Eduardo Julian. All rights reserved.
+;; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+;; If a copy of the MPL was not distributed with this file,
+;; You can obtain one at http://mozilla.org/MPL/2.0/.
+
+(ns leiningen.luxc.compiler
+ (:refer-clojure :exclude [compile])
+ (:require [leiningen.core.classpath :as classpath]
+ (leiningen.luxc [utils :as &utils]
+ [packager :as &packager])))
+
+(defn compile [project]
+ (if-let [program-module (get-in project [:lux :program])]
+ (do (&utils/run-process (&utils/compile-path project program-module (get project :source-paths (list)))
+ nil
+ "[COMPILATION BEGIN]"
+ "[COMPILATION END]")
+ (&packager/package project program-module (get project :resource-paths (list))))
+ (println "Please provide a program main module in [:lux :program]")))
diff --git a/lux-lein/src/leiningen/luxc/packager.clj b/lux-lein/src/leiningen/luxc/packager.clj
new file mode 100644
index 000000000..e7b1d71d8
--- /dev/null
+++ b/lux-lein/src/leiningen/luxc/packager.clj
@@ -0,0 +1,212 @@
+;; Copyright (c) Eduardo Julian. All rights reserved.
+;; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+;; If a copy of the MPL was not distributed with this file,
+;; You can obtain one at http://mozilla.org/MPL/2.0/.
+
+(ns leiningen.luxc.packager
+ (:require [clojure.string :as string]
+ [leiningen.core.classpath :as classpath]
+ [leiningen.uberjar]
+ [leiningen.luxc.utils :as &utils])
+ (:import (java.io InputStream
+ File
+ FileInputStream
+ FileOutputStream
+ BufferedInputStream
+ ByteArrayInputStream
+ ByteArrayOutputStream)
+ (java.util.jar Manifest
+ Attributes$Name
+ JarEntry
+ JarInputStream
+ JarOutputStream
+ )))
+
+;; [Utils]
+(def ^:private kilobyte 1024)
+(def ^:private buffer-size (* 10 kilobyte))
+
+(defn ^:private manifest
+ "(-> Project Text Bool Manifest)"
+ [project module includes-android?]
+ (doto (new Manifest)
+ (-> .getMainAttributes
+ (doto (-> (.put Attributes$Name/MAIN_CLASS (str module "._"))
+ (->> (when (not includes-android?))))
+ (.put Attributes$Name/MANIFEST_VERSION "1.0")
+ (.put (new Attributes$Name "LUX_JAR") "true")
+ (-> (.put (new Attributes$Name name) real-v)
+ (->> (doseq [[name v] (get project :manifest)
+ :let [real-v (if (string? v) v (v project))]])))))))
+
+(defn ^:private write-class!
+ "(-> Text File JarOutputStream Null)"
+ [^String path ^File file ^JarOutputStream out]
+ (with-open [in (new BufferedInputStream (new FileInputStream file))]
+ (let [buffer (byte-array buffer-size)]
+ (doto out
+ (.putNextEntry (new JarEntry (str path "/" (.getName file))))
+ (-> (.write buffer 0 bytes-read)
+ (->> (when (not= -1 bytes-read))
+ (loop [bytes-read (.read in buffer)])))
+ (.flush)
+ (.closeEntry)
+ ))
+ ))
+
+(defn ^:private write-module!
+ "(-> File JarOutputStream Null)"
+ [^File file ^JarOutputStream out output-dir]
+ (let [output-dir-size (inc (.length output-dir))
+ module-name (.substring (.getPath file) output-dir-size)
+ inner-files (.listFiles file)
+ inner-modules (filter #(.isDirectory ^File %) inner-files)
+ inner-classes (filter #(not (.isDirectory ^File %)) inner-files)]
+ (doseq [$class inner-classes]
+ (write-class! module-name $class out))
+ (doseq [$module inner-modules]
+ (write-module! $module out output-dir))))
+
+(defn ^:private write-resources!
+ "(-> JarOutputStream (List Text) Null)"
+ [^JarOutputStream out resources-dirs]
+ (doseq [resources-dir resources-dirs
+ :let [resources-dir (new File resources-dir)]
+ :when (.exists resources-dir)
+ ^File res (.listFiles resources-dir)
+ :let [buffer (byte-array buffer-size)]]
+ (with-open [in (->> res (new FileInputStream) (new BufferedInputStream))]
+ (doto out
+ (.putNextEntry (new JarEntry (.getName res)))
+ (-> (.write buffer 0 bytes-read)
+ (->> (when (not= -1 bytes-read))
+ (loop [bytes-read (.read in buffer)])))
+ (.flush)
+ (.closeEntry))
+ )))
+
+(let [init-capacity (* 100 1024)
+ buffer-size 1024]
+ (defn ^:private ^"[B" read-stream [^InputStream is]
+ (let [buffer (byte-array buffer-size)]
+ (with-open [os (new ByteArrayOutputStream init-capacity)]
+ (loop [bytes-read (.read is buffer 0 buffer-size)]
+ (when (not= -1 bytes-read)
+ (do (.write os buffer 0 bytes-read)
+ (recur (.read is buffer 0 buffer-size)))))
+ (.toByteArray os)))))
+
+(defn ^:private add-jar! [^File jar-file project !all-jar-files]
+ (with-open [is (->> jar-file (new FileInputStream) (new JarInputStream))]
+ (loop [^JarEntry entry (.getNextJarEntry is)]
+ (when entry
+ (let [entry-name (.getName entry)]
+ (if (and (not (.isDirectory entry))
+ (not (.startsWith entry-name "META-INF/maven/"))
+ (not (some (fn [exclusion]
+ (re-find exclusion entry-name))
+ (get project :uberjar-exclusions))))
+ (let [entry-data (read-stream is)
+ entry-data (or (some (fn [[pattern [read fuse write]]]
+ (let [matches? (if (string? pattern)
+ (= pattern entry-name)
+ (re-find pattern entry-name))]
+ (when matches?
+ (let [os (new ByteArrayOutputStream 1024)
+ [_data _entry] (get @!all-jar-files entry-name [(byte-array 0) nil])
+ _ (write os (fuse (read (new ByteArrayInputStream _data))
+ (read (new ByteArrayInputStream entry-data))))]
+ (.toByteArray os)))))
+ (eval (get project :uberjar-merge-with)))
+ entry-data)]
+ (swap! !all-jar-files assoc entry-name [entry-data entry])
+ (recur (.getNextJarEntry is)))
+ (recur (.getNextJarEntry is))))
+ ))))
+
+(def default-manifest-file "./AndroidManifest.xml")
+
+;; [Resources]
+(defn package
+ "(-> Text (List Text) Null)"
+ [project module resources-dirs]
+ (let [output-dir (get-in project [:lux :target] &utils/output-dir)
+ output-package (str (get-in project [:lux :target] &utils/output-dir) "/"
+ (get project :jar-name &utils/output-package))
+ !all-jar-files (atom {})
+ includes-android? (boolean (some #(-> % first (= 'com.google.android/android))
+ (get project :dependencies)))
+ project* (-> project
+ (update-in [:dependencies] (fn [_deps]
+ ;; Skip the last two,
+ ;; because they are:
+ ;; tools.nrepl-0.2.12.jar and
+ ;; clojure-complete-0.2.4.jar
+ ;; and they belong to Leiningen.
+ (take (- (count _deps) 2) _deps))))
+ deps (->> project*
+ (classpath/resolve-managed-dependencies :dependencies :managed-dependencies)
+ (map #(.getAbsolutePath ^File %)))]
+ (do (.delete (new File output-package))
+ (with-open [out (new JarOutputStream
+ (->> output-package (new File) (new FileOutputStream))
+ (manifest project module includes-android?))]
+ (do (doseq [$group (.listFiles (new File output-dir))]
+ (write-module! $group out output-dir))
+ (when (not (get-in project [:lux :android]))
+ (write-resources! out resources-dirs))
+ (doseq [^String file-path deps]
+ (add-jar! (new File file-path) project !all-jar-files))
+ (doseq [[_ [entry-data entry]] @!all-jar-files]
+ (doto out
+ (.putNextEntry (doto entry (.setCompressedSize -1)))
+ (.write entry-data 0 (alength entry-data))
+ (.flush)
+ (.closeEntry)))
+ nil))
+ (when (get-in project [:lux :android])
+ (let [output-dex "classes.dex"
+ _ (do (.delete (new File output-dex))
+ (&utils/run-process (str "dx --dex --output=" output-dex " " output-package)
+ (new File (get-in project [:lux :target] &utils/output-dir))
+ "[DX BEGIN]"
+ "[DX END]"))
+ manifest-path (get-in project [:lux :android :manifest] default-manifest-file)
+ sdk-path (get-in project [:lux :android :sdk])
+ android-path (str sdk-path "/platforms/android-" (get-in project [:lux :android :version]) "/android.jar")
+ _ (assert (.exists (new File android-path))
+ (str "Can't find Android JAR: " android-path))
+ output-apk-unaligned (string/replace output-package #"\.jar$" ".apk.unaligned")
+ output-apk (string/replace output-package #"\.jar$" ".apk")
+ current-working-dir (.getCanonicalPath (new File "."))
+ _ (do (&utils/run-process (str "aapt package -f -M " manifest-path " -I " android-path " -F " output-apk-unaligned
+ (apply str " " (interleave (repeat (count resources-dirs)
+ "-A ")
+ (filter #(.exists (new File %))
+ resources-dirs)))
+ (apply str " " (interleave (repeat (count resources-dirs)
+ "-S ")
+ (->> (get-in project [:lux :android :resources] ["android-resources"])
+ (map (partial str current-working-dir "/"))
+ (filter #(.exists (new File %)))))))
+ nil
+ "[AAPT PACKAGE BEGIN]"
+ "[AAPT PACKAGE END]")
+ (&utils/run-process (str "aapt add -f " output-apk-unaligned " " output-dex)
+ (new File (get-in project [:lux :target] &utils/output-dir))
+ "[AAPT ADD BEGIN]"
+ "[AAPT ADD END]")
+ (when-let [path (get-in project [:lux :android :keystore :path])]
+ (when-let [alias (get-in project [:lux :android :keystore :alias])]
+ (when-let [password (get-in project [:lux :android :keystore :password])]
+ (&utils/run-process (str "jarsigner -storepass " password " -keystore " path " " output-apk-unaligned " " alias)
+ nil
+ "[JARSIGNER BEGIN]"
+ "[JARSIGNER END]"))))
+ (&utils/run-process (str "zipalign 4 " output-apk-unaligned " " output-apk)
+ nil
+ "[ZIPALIGN BEGIN]"
+ "[ZIPALIGN END]")
+ )
+ ]
+ nil)))))
diff --git a/lux-lein/src/leiningen/luxc/repl.clj b/lux-lein/src/leiningen/luxc/repl.clj
new file mode 100644
index 000000000..2bfb281e6
--- /dev/null
+++ b/lux-lein/src/leiningen/luxc/repl.clj
@@ -0,0 +1,35 @@
+;; Copyright (c) Eduardo Julian. All rights reserved.
+;; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+;; If a copy of the MPL was not distributed with this file,
+;; You can obtain one at http://mozilla.org/MPL/2.0/.
+
+(ns leiningen.luxc.repl
+ (:require [leiningen.core.classpath :as classpath]
+ [leiningen.luxc.utils :as &utils])
+ (:import (java.io InputStreamReader
+ BufferedReader
+ PrintStream)))
+
+(defn repl [project]
+ (println (&utils/repl-path project (:source-paths project)))
+ ;; (let [process (.exec (Runtime/getRuntime) (&utils/repl-path project (:source-paths project)))]
+ ;; (with-open [std-in (->> System/in (new InputStreamReader) (new BufferedReader))
+ ;; process-in (->> process .getOutputStream (new PrintStream))
+ ;; process-out (->> process .getInputStream (new InputStreamReader) (new BufferedReader))
+ ;; process-err (->> process .getErrorStream (new InputStreamReader) (new BufferedReader))]
+ ;; (loop []
+ ;; (do (loop []
+ ;; (when (.ready process-out)
+ ;; (println (.readLine process-out))
+ ;; (recur)))
+ ;; (loop [had-error? false]
+ ;; (if (.ready process-out)
+ ;; (do (println (.readLine process-err))
+ ;; (recur true))
+ ;; (when had-error?
+ ;; (System/exit 1))))
+ ;; (when-let [input (.readLine std-in)]
+ ;; (do (.println process-in input)
+ ;; (recur)))))
+ ;; ))
+ )
diff --git a/lux-lein/src/leiningen/luxc/test.clj b/lux-lein/src/leiningen/luxc/test.clj
new file mode 100644
index 000000000..a1b5a830c
--- /dev/null
+++ b/lux-lein/src/leiningen/luxc/test.clj
@@ -0,0 +1,27 @@
+;; Copyright (c) Eduardo Julian. All rights reserved.
+;; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+;; If a copy of the MPL was not distributed with this file,
+;; You can obtain one at http://mozilla.org/MPL/2.0/.
+
+(ns leiningen.luxc.test
+ (:refer-clojure :exclude [test])
+ (:require [leiningen.core.classpath :as classpath]
+ (leiningen.luxc [utils :as &utils]
+ [packager :as &packager])))
+
+(defn test [project]
+ (if-let [tests-module (get-in project [:lux :tests])]
+ (do (&utils/run-process (&utils/compile-path project tests-module (concat (:test-paths project) (:source-paths project)))
+ nil
+ "[COMPILATION BEGIN]"
+ "[COMPILATION END]")
+ (let [java-cmd (get project :java-cmd "java")
+ jvm-opts (->> (get project :jvm-opts) (interpose " ") (reduce str ""))
+ output-package (str (get-in project [:lux :target] &utils/output-dir) "/"
+ (get project :jar-name &utils/output-package))]
+ (do (&packager/package project tests-module (get project :resource-paths (list)))
+ (&utils/run-process (str java-cmd " " jvm-opts " -jar " output-package)
+ nil
+ "[TEST BEGIN]"
+ "[TEST END]"))))
+ (println "Please provide a test module in [:lux :tests]")))
diff --git a/lux-lein/src/leiningen/luxc/utils.clj b/lux-lein/src/leiningen/luxc/utils.clj
new file mode 100644
index 000000000..bae02d365
--- /dev/null
+++ b/lux-lein/src/leiningen/luxc/utils.clj
@@ -0,0 +1,97 @@
+;; Copyright (c) Eduardo Julian. All rights reserved.
+;; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+;; If a copy of the MPL was not distributed with this file,
+;; You can obtain one at http://mozilla.org/MPL/2.0/.
+
+(ns leiningen.luxc.utils
+ (:refer-clojure :exclude [compile])
+ (:require [leiningen.core.classpath :as classpath])
+ (:import (java.io File
+ InputStreamReader
+ BufferedReader)))
+
+(def ^:const ^String output-dir "target/jvm")
+(def ^:const ^String output-package "program.jar")
+
+(def ^:private unit-separator (str (char 31)))
+
+(def ^:private vm-options "-server -Xms2048m -Xmx2048m -XX:+OptimizeStringConcat")
+
+(defn compile-path [project module source-paths]
+ (let [output-dir (get-in project [:lux :target] output-dir)
+ jar-paths (->> ^java.net.URLClassLoader (ClassLoader/getSystemClassLoader)
+ (.getURLs)
+ (map #(.getFile ^java.net.URL %))
+ (filter #(.endsWith ^String % ".jar")))
+ compiler-path (some (fn [^:private path]
+ (if (.contains path "com/github/luxlang/luxc-jvm")
+ path
+ nil))
+ jar-paths)
+ stdlib-path (some (fn [^:private path]
+ (if (.contains path "com/github/luxlang/lux-stdlib")
+ path
+ nil))
+ jar-paths)
+ deps-paths (filter (fn [^:private path]
+ (or (.contains path "org/ow2/asm/asm-all")
+ (.contains path "org/clojure/core.match")
+ (.contains path "org/clojure/clojure")))
+ jar-paths)
+ sdk-path (get-in project [:lux :android :sdk])
+ android-path (str sdk-path "/platforms/android-" (get-in project [:lux :android :version]) "/android.jar")
+ deps-paths (if (.exists (new File android-path))
+ (cons android-path deps-paths)
+ deps-paths)]
+ (let [class-path (->> (classpath/get-classpath project)
+ (filter #(.endsWith % ".jar"))
+ (concat deps-paths)
+ (list* stdlib-path)
+ (interpose java.io.File/pathSeparator)
+ (reduce str ""))
+ java-cmd (get project :java-cmd "java")
+ jvm-opts (->> (get project :jvm-opts) (interpose " ") (reduce str ""))]
+ (str java-cmd " " jvm-opts " " vm-options " -cp " (str compiler-path ":" class-path)
+ " lux release " module
+ " " (->> (get project :resource-paths (list)) (interpose unit-separator) (apply str))
+ " " (->> source-paths (interpose unit-separator) (apply str))
+ " " output-dir))))
+
+(defn repl-path [project source-paths]
+ (let [jar-paths (->> ^java.net.URLClassLoader (ClassLoader/getSystemClassLoader)
+ (.getURLs)
+ (map #(.getFile ^java.net.URL %))
+ (filter #(.endsWith ^String % ".jar")))
+ compiler-path (some (fn [^:private path]
+ (if (.contains path "com/github/luxlang/luxc-jvm")
+ path
+ nil))
+ jar-paths)
+ deps-paths (filter (fn [^:private path]
+ (or (.contains path "org/ow2/asm/asm-all")
+ (.contains path "org/clojure/core.match")
+ (.contains path "org/clojure/clojure")))
+ jar-paths)]
+ (let [class-path (->> (classpath/get-classpath project) (filter #(.endsWith % ".jar")) (concat deps-paths) (interpose ":") (reduce str ""))
+ java-cmd (get project :java-cmd "java")
+ jvm-opts (->> (get project :jvm-opts) (interpose " ") (reduce str ""))]
+ (str java-cmd " " jvm-opts " " vm-options " -cp " (str compiler-path ":" class-path)
+ " lux repl " (->> source-paths (interpose unit-separator) (apply str))))))
+
+(defn run-process [command working-directory pre post]
+ (let [process (.exec (Runtime/getRuntime) command nil working-directory)]
+ (with-open [std-out (->> process .getInputStream (new InputStreamReader) (new BufferedReader))
+ std-err (->> process .getErrorStream (new InputStreamReader) (new BufferedReader))]
+ (println pre)
+ (loop [line (.readLine std-out)]
+ (when line
+ (println line)
+ (recur (.readLine std-out))))
+ (loop [had-error? false
+ line (.readLine std-err)]
+ (if line
+ (do (println line)
+ (recur true (.readLine std-err)))
+ (when had-error?
+ (System/exit 1))))
+ (println post))))