(.using [library [lux {"-" Module Definition} ["[0]" ffi {"+" import: do_to}] [abstract ["[0]" monad {"+" Monad do}]] [control ["[0]" maybe ("[1]#[0]" functor)] ["[0]" try {"+" Try}]] [data ["[0]" binary {"+" Binary}] ["[0]" text ["%" format {"+" format}]] [collection ["[0]" sequence] ["[0]" list ("[1]#[0]" functor)] ["[0]" dictionary] ["[0]" set {"+" Set}]]] [math [number ["n" nat] ["i" int]]] [target [jvm [encoding ["[0]" name]]]] [world ["[0]" file]]]] [program [compositor ["[0]" static {"+" Static}]]] ["[0]" // {"+" Packager} [// ["[0]" archive {"+" Output} ["[0]" module] ["[0]" descriptor {"+" Module}] ["[0]" artifact] ["[0]" unit]] ["[0]" cache "_" ["[1]/[0]" module] ["[1]/[0]" artifact]] ["[0]" io "_" ["[1]" archive]] [// [language ["$" lux [phase [generation [jvm ["[0]" runtime {"+" Definition}]]]]]]]]]) (import: java/lang/Object) (import: java/lang/String) (import: java/util/jar/Attributes ["[1]::[0]" (put [java/lang/Object java/lang/Object] "?" java/lang/Object)]) (import: java/util/jar/Attributes$Name ["[1]::[0]" ("static" MAIN_CLASS java/util/jar/Attributes$Name) ("static" MANIFEST_VERSION java/util/jar/Attributes$Name)]) (import: java/util/jar/Manifest ["[1]::[0]" (new []) (getMainAttributes [] java/util/jar/Attributes)]) (import: java/io/Flushable ["[1]::[0]" (flush [] void)]) (import: java/io/Closeable ["[1]::[0]" (close [] void)]) (import: java/io/OutputStream ["[1]::[0]" (write [[byte] int int] void)]) (import: java/io/ByteArrayOutputStream ["[1]::[0]" (new [int]) (toByteArray [] [byte])]) (import: java/util/zip/ZipEntry ["[1]::[0]" (getName [] java/lang/String) (isDirectory [] boolean) (getSize [] long)]) (import: java/util/zip/ZipOutputStream ["[1]::[0]" (write [[byte] int int] void) (closeEntry [] void)]) (import: java/util/jar/JarEntry ["[1]::[0]" (new [java/lang/String])]) (import: java/util/jar/JarOutputStream ["[1]::[0]" (new [java/io/OutputStream java/util/jar/Manifest]) (putNextEntry [java/util/zip/ZipEntry] "try" void)]) (import: java/io/ByteArrayInputStream ["[1]::[0]" (new [[byte]])]) (import: java/io/InputStream ["[1]::[0]" (read [[byte] int int] int)]) (import: java/util/jar/JarInputStream ["[1]::[0]" (new [java/io/InputStream]) (getNextJarEntry [] "try" "?" java/util/jar/JarEntry)]) (def: byte 1) ... https://en.wikipedia.org/wiki/Kibibyte (def: kibi_byte (n.* 1,024 byte)) ... https://en.wikipedia.org/wiki/Mebibyte (def: mebi_byte (n.* 1,024 kibi_byte)) (def: manifest_version "1.0") (def: (manifest program) (-> unit.ID java/util/jar/Manifest) (let [manifest (java/util/jar/Manifest::new)] (exec (do_to (java/util/jar/Manifest::getMainAttributes manifest) (java/util/jar/Attributes::put (java/util/jar/Attributes$Name::MAIN_CLASS) (|> program runtime.class_name name.internal name.external)) (java/util/jar/Attributes::put (java/util/jar/Attributes$Name::MANIFEST_VERSION) ..manifest_version)) manifest))) (def: (write_class static module artifact custom content sink) (-> Static module.ID artifact.ID (Maybe Text) Binary java/util/jar/JarOutputStream (Try java/util/jar/JarOutputStream)) (let [class_path (|> custom (maybe#each (|>> name.internal name.read)) (maybe.else (runtime.class_name [module artifact])) (text.replaced "." "/") (text.suffix (value@ static.#artifact_extension static)))] (do try.monad [_ (java/util/jar/JarOutputStream::putNextEntry (java/util/jar/JarEntry::new class_path) sink)] (in (do_to sink (java/util/zip/ZipOutputStream::write content +0 (.int (binary.size content))) (java/io/Flushable::flush) (java/util/zip/ZipOutputStream::closeEntry)))))) (def: (write_module static necessary_dependencies [module output] sink) (-> Static (Set unit.ID) [module.ID Output] java/util/jar/JarOutputStream (Try java/util/jar/JarOutputStream)) (let [! try.monad] (monad.mix try.monad (function (_ [artifact custom content] sink) (if (set.member? necessary_dependencies [module artifact]) (..write_class static module artifact custom content sink) (# ! in sink))) sink (sequence.list output)))) (def: (read_jar_entry_with_unknown_size input) (-> java/util/jar/JarInputStream [Nat Binary]) (let [chunk (binary.empty ..mebi_byte) chunk_size (.int ..mebi_byte) buffer (java/io/ByteArrayOutputStream::new chunk_size)] (loop [so_far 0] (case (java/io/InputStream::read chunk 0 chunk_size input) -1 [so_far (java/io/ByteArrayOutputStream::toByteArray buffer)] bytes_read (exec (java/io/OutputStream::write chunk +0 bytes_read buffer) (again (|> bytes_read .nat (n.+ so_far)))))))) (def: (read_jar_entry_with_known_size expected_size input) (-> Nat java/util/jar/JarInputStream [Nat Binary]) (let [buffer (binary.empty expected_size)] (loop [so_far 0] (let [so_far' (|> input (java/io/InputStream::read buffer (.int so_far) (.int (n.- so_far expected_size))) .nat (n.+ so_far))] (if (n.= expected_size so_far') [expected_size buffer] (again so_far')))))) (def: (read_jar_entry entry input) (-> java/util/jar/JarEntry java/util/jar/JarInputStream [Nat Binary]) (case (java/util/zip/ZipEntry::getSize entry) -1 (..read_jar_entry_with_unknown_size input) entry_size (..read_jar_entry_with_known_size (.nat entry_size) input))) (def: (write_host_dependency jar [entries duplicates sink]) (-> Binary [(Set file.Path) (Set file.Path) java/util/jar/JarOutputStream] (Try [(Set file.Path) (Set file.Path) java/util/jar/JarOutputStream])) (let [input (|> jar java/io/ByteArrayInputStream::new java/util/jar/JarInputStream::new)] (loop [entries entries duplicates duplicates sink sink] (case (java/util/jar/JarInputStream::getNextJarEntry input) {try.#Failure error} {try.#Failure error} {try.#Success ?entry} (case ?entry {.#None} (exec (java/io/Closeable::close input) {try.#Success [entries duplicates sink]}) {.#Some entry} (let [entry_path (java/util/zip/ZipEntry::getName entry) entry_size (java/util/zip/ZipEntry::getSize entry)] (if (not (or (java/util/zip/ZipEntry::isDirectory entry) (or (text.starts_with? "META-INF/maven/" entry_path) (text.starts_with? "META-INF/leiningen/" entry_path)) (or (text.ends_with? ".SF" entry_path) (text.ends_with? ".DSA" entry_path)))) (case (java/util/jar/JarOutputStream::putNextEntry (java/util/jar/JarEntry::new entry_path) sink) {try.#Failure error} (again entries (set.has entry_path duplicates) sink) {try.#Success _} (let [[entry_size entry_data] (read_jar_entry entry input)] (again (set.has entry_path entries) duplicates (do_to sink (java/util/zip/ZipOutputStream::write entry_data +0 (.int entry_size)) (java/io/Flushable::flush) (java/util/zip/ZipOutputStream::closeEntry))))) (again entries duplicates sink)))))))) (def: .public (package static) (-> Static Packager) (function (_ host_dependencies archive program) (do [! try.monad] [.let [necessary_dependencies (cache/artifact.necessary_dependencies archive)] order (cache/module.load_order $.key archive) .let [buffer (java/io/ByteArrayOutputStream::new (.int ..mebi_byte))] sink (|> order (list#each (function (_ [module [module_id entry]]) [module_id (value@ archive.#output entry)])) (monad.mix ! (..write_module static necessary_dependencies) (java/util/jar/JarOutputStream::new buffer (..manifest program)))) [entries duplicates sink] (|> host_dependencies dictionary.values (monad.mix ! ..write_host_dependency [(set.empty text.hash) (set.empty text.hash) sink])) .let [_ (do_to sink (java/io/Flushable::flush) (java/io/Closeable::close))]] (in (|> buffer java/io/ByteArrayOutputStream::toByteArray {.#Left})))))