Skip to content

Commit 55a79ee

Browse files
committed
process - TBUILD-1 Fix blocked buffering when capturing large output
1 parent 65b3c45 commit 55a79ee

File tree

3 files changed

+56
-35
lines changed

3 files changed

+56
-35
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Changelog
22
===========
33

4+
* next
5+
* process - TBUILD-1 Fix blocked buffering when capturing large output
46
* v0.10.7 573711e on Feb 8, 2025
57
* write-pom - add warning if pom-data ignored because src-pom template exists
68
* Update deps to latest

src/main/clojure/clojure/tools/build/tasks/process.clj

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,17 @@
99
(ns clojure.tools.build.tasks.process
1010
(:require
1111
[clojure.java.io :as jio]
12+
[clojure.java.process :as proc]
1213
[clojure.tools.deps :as deps]
1314
[clojure.tools.build.api :as api]
1415
[clojure.string :as str])
1516
(:import
16-
[java.io InputStream StringWriter File]
17-
[java.lang ProcessBuilder ProcessBuilder$Redirect]
18-
[java.util List]))
19-
20-
(defn- copy-stream
21-
[^InputStream input-stream]
22-
(let [writer (StringWriter.)]
23-
(jio/copy input-stream writer)
24-
(let [s (.toString writer)]
25-
(when-not (zero? (.length s))
26-
s))))
17+
[java.io InputStream StringWriter File]))
18+
19+
(set! *warn-on-reflection* true)
20+
21+
(defn- trim-blank [^String s]
22+
(if (str/blank? s) nil s))
2723

2824
(defn process
2925
"Exec the command made from command-args, redirect out and err as directed,
@@ -48,30 +44,36 @@
4844
:or {dir ".", out :inherit, err :inherit} :as opts}]
4945
(when (not (seq command-args))
5046
(throw (ex-info "process missing required arg :command-args" opts)))
51-
(let [pb (ProcessBuilder. ^List command-args)]
52-
(.directory pb (api/resolve-path (or dir ".")))
53-
(case out
54-
:inherit (.redirectOutput pb ProcessBuilder$Redirect/INHERIT)
55-
:write (.redirectOutput pb (ProcessBuilder$Redirect/to (jio/file (api/resolve-path out-file))))
56-
:append (.redirectOutput pb (ProcessBuilder$Redirect/appendTo (jio/file (api/resolve-path out-file))))
57-
:capture (.redirectOutput pb ProcessBuilder$Redirect/PIPE)
58-
:ignore (.redirectOutput pb ProcessBuilder$Redirect/PIPE))
59-
(case err
60-
:inherit (.redirectError pb ProcessBuilder$Redirect/INHERIT)
61-
:write (.redirectError pb (ProcessBuilder$Redirect/to (jio/file (api/resolve-path err-file))))
62-
:append (.redirectError pb (ProcessBuilder$Redirect/appendTo (jio/file (api/resolve-path err-file))))
63-
:capture (.redirectError pb ProcessBuilder$Redirect/PIPE)
64-
:ignore (.redirectError pb ProcessBuilder$Redirect/PIPE))
65-
(when env
66-
(let [pb-env (.environment pb)]
67-
(run! (fn [[k v]] (.put pb-env k v)) env)))
68-
(let [proc (.start pb)
69-
exit (.waitFor proc)
70-
out-str (when (= out :capture) (copy-stream (.getInputStream proc)))
71-
err-str (when (= err :capture) (copy-stream (.getErrorStream proc)))]
72-
(cond-> {:exit exit}
73-
out-str (assoc :out out-str)
74-
err-str (assoc :err err-str)))))
47+
(let [stream-opt (fn [opt file]
48+
(case opt
49+
:inherit :inherit
50+
:write (proc/to-file (api/resolve-path file))
51+
:append (proc/to-file (api/resolve-path file) :append true)
52+
:ignore :discard
53+
(:capture :pipe) :pipe))
54+
proc-opts {:dir (api/resolve-path (or dir "."))
55+
:out (stream-opt out out-file)
56+
:err (stream-opt err err-file)
57+
:env env}
58+
proc (apply proc/start proc-opts command-args)
59+
out-f (when (= out :capture) (proc/io-task #(slurp (proc/stdout proc))))
60+
err-f (when (= err :capture) (proc/io-task #(slurp (proc/stderr proc))))
61+
exit (deref (proc/exit-ref proc))
62+
out-str (when out-f (trim-blank @out-f))
63+
err-str (when err-f (trim-blank @err-f))]
64+
(cond-> {:exit exit}
65+
out-str (assoc :out out-str)
66+
err-str (assoc :err err-str))))
67+
68+
(comment
69+
(api/process {:command-args ["ls" "-l"]})
70+
(api/process {:command-args ["git" "log"] :out :ignore})
71+
(api/process {:command-args ["java" "-version"] :err :capture})
72+
(api/process {:command-args ["java" "--version"] :out :capture})
73+
(api/process {:env {"FOO" "hi"}
74+
:command-args ["echo" "$FOO"]
75+
:out :capture})
76+
)
7577

7678
(defn- need-cp-file
7779
[os-name java-version command-length]

src/test/clojure/clojure/tools/build/tasks/test_process.clj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@
1313
[clojure.tools.build.api :as api]
1414
[clojure.tools.build.tasks.process :as process]))
1515

16+
(deftest test-large-process-output
17+
(is (string? (api/git-process {:git-args ["log"]}))))
18+
19+
(deftest test-capture
20+
(is (string? (:out (api/process {:command-args ["java" "--version"]
21+
:out :capture}))))
22+
(is (string? (:err (api/process {:command-args ["java" "-version"]
23+
:err :capture})))))
24+
25+
(deftest test-env
26+
(when-not (#'process/windows?)
27+
(is (= "hi\n"
28+
(:out
29+
(api/process {:env {"FOO" "hi"}
30+
:command-args ["/bin/bash" "-c" "echo $FOO"]
31+
:out :capture}))))))
32+
1633
(deftest test-need-cp-file
1734
(let [f #'process/need-cp-file]
1835
(are [os java length expected]

0 commit comments

Comments
 (0)