Skip to content

Commit a11d2ff

Browse files
committed
Logging sketches to separate stream & small refactor
1 parent 5729e88 commit a11d2ff

File tree

3 files changed

+101
-53
lines changed

3 files changed

+101
-53
lines changed

app/src/processing/app/gradle/GradleJob.kt

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.compose.runtime.mutableStateOf
55
import com.sun.jdi.VirtualMachine
66
import kotlinx.coroutines.CoroutineScope
77
import kotlinx.coroutines.Dispatchers
8+
import kotlinx.coroutines.Job
89
import kotlinx.coroutines.launch
910
import org.gradle.tooling.BuildCancelledException
1011
import org.gradle.tooling.BuildLauncher
@@ -20,6 +21,8 @@ import processing.app.Base
2021
import processing.app.Messages
2122
import processing.app.Platform
2223
import processing.app.ui.EditorStatus
24+
import java.io.PrintStream
25+
import java.net.ServerSocket
2326

2427
class GradleJob{
2528
enum class State{
@@ -36,16 +39,17 @@ class GradleJob{
3639
val state = mutableStateOf(State.NONE)
3740
val vm = mutableStateOf<VirtualMachine?>(null)
3841
val problems = mutableStateListOf<ProblemEvent>()
42+
val jobs = mutableStateListOf<Job>()
3943

4044
private val scope = CoroutineScope(Dispatchers.IO)
4145
private val cancel = GradleConnector.newCancellationTokenSource()
4246

4347
fun start() {
4448
val folder = service?.sketch?.folder ?: throw IllegalStateException("Sketch folder is not set")
45-
scope.launch {
49+
launchJob {
4650
try {
4751
state.value = State.BUILDING
48-
service?.editor?.statusMessage("Building sketch", EditorStatus.NOTICE)
52+
service?.editor?.statusMessage("Connecting to Gradle", EditorStatus.NOTICE)
4953

5054
GradleConnector.newConnector()
5155
.forProjectDirectory(folder)
@@ -56,14 +60,20 @@ class GradleJob{
5660
}
5761
}
5862
.connect()
63+
.apply {
64+
service?.editor?.statusMessage("Building sketch", EditorStatus.NOTICE)
65+
}
5966
.newBuild()
6067
.apply {
6168
configure()
6269
withCancellationToken(cancel.token())
6370
addStateListener()
6471
addDebugging()
65-
setStandardOutput(System.out)
66-
setStandardError(System.err)
72+
addLogserver()
73+
if(Base.DEBUG) {
74+
setStandardOutput(System.out)
75+
setStandardError(System.err)
76+
}
6777
run()
6878
}
6979
}catch (e: Exception){
@@ -80,12 +90,12 @@ class GradleJob{
8090

8191
if (skip.any { it.isInstance(e) }) {
8292
Messages.log("Gradle job error: $errors")
83-
return@launch
93+
return@launchJob
8494
}
8595

8696
if(state.value == State.RUNNING){
8797
Messages.log("Gradle job error: $errors")
88-
return@launch
98+
return@launchJob
8999
}
90100

91101
// An error occurred during the build process
@@ -98,10 +108,16 @@ class GradleJob{
98108
}
99109
}
100110
}
111+
fun launchJob(block: suspend CoroutineScope.() -> Unit){
112+
val job = scope.launch { block() }
113+
jobs.add(job)
114+
}
101115

102116
fun cancel(){
103117
cancel.cancel()
118+
jobs.forEach(Job::cancel)
104119
}
120+
105121
private fun BuildLauncher.addStateListener(){
106122
addProgressListener(ProgressListener { event ->
107123
if(event is TaskStartEvent) {
@@ -167,13 +183,42 @@ class GradleJob{
167183
})
168184
}
169185

186+
fun addLogserver(){
187+
launchJob {
188+
startLogServer(service?.logPort ?: 5006, System.out)
189+
}
190+
launchJob{
191+
startLogServer(service?.errPort ?: 5007, System.err)
192+
}
193+
}
194+
fun startLogServer(port: Int, target: PrintStream){
195+
val server = ServerSocket(port)
196+
Messages.log("Log server started on port $port")
197+
val client = server.accept()
198+
Messages.log("Log server client connected")
199+
200+
val reader = client.getInputStream().bufferedReader()
201+
try {
202+
reader.forEachLine { line ->
203+
if (line.isNotBlank()) {
204+
target.println(line)
205+
}
206+
}
207+
} catch (e: Exception) {
208+
Messages.log("Error while reading from log server: ${e.message}")
209+
} finally {
210+
client.close()
211+
server.close()
212+
}
213+
}
214+
170215
fun BuildLauncher.addDebugging() {
171216
addProgressListener(ProgressListener { event ->
172217
if (event !is TaskStartEvent) return@ProgressListener
173218
if (event.descriptor.name != ":run") return@ProgressListener
174219

175-
scope.launch {
176-
val debugger = Debugger.connect(service?.debugPort) ?: return@launch
220+
launchJob {
221+
val debugger = Debugger.connect(service?.debugPort) ?: return@launchJob
177222
vm.value = debugger
178223
val exceptions = Exceptions(debugger, service?.editor)
179224
exceptions.listen()

app/src/processing/app/gradle/GradleService.kt

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@ package processing.app.gradle
22

33
import androidx.compose.runtime.mutableStateListOf
44
import androidx.compose.runtime.mutableStateOf
5-
import androidx.compose.runtime.snapshotFlow
6-
import kotlinx.coroutines.CoroutineScope
7-
import kotlinx.coroutines.Dispatchers
8-
import kotlinx.coroutines.launch
95
import org.gradle.tooling.BuildLauncher
10-
import processing.app.Base
11-
import processing.app.Language
6+
import processing.app.Base.DEBUG
7+
import processing.app.Base.getSketchbookFolder
8+
import processing.app.Base.getVersionName
9+
import processing.app.Language.text
1210
import processing.app.Messages
1311
import processing.app.Mode
1412
import processing.app.Platform
13+
import processing.app.Platform.getContentFile
14+
import processing.app.Platform.getSettingsFolder
1515
import processing.app.Preferences
1616
import processing.app.Sketch
1717
import processing.app.ui.Editor
18-
import java.io.*
1918
import kotlin.io.path.createTempDirectory
2019
import kotlin.io.path.deleteIfExists
2120
import kotlin.io.path.writeText
@@ -48,51 +47,51 @@ class GradleService(
4847

4948
val jobs = mutableStateListOf<GradleJob>()
5049
val workingDir = createTempDirectory()
50+
5151
val debugPort = (30_000..60_000).random()
52+
val logPort = debugPort + 1
53+
val errPort = logPort + 1
5254

5355
fun run(){
54-
startAction("run")
56+
startJob("run")
5557
}
5658

5759
fun export(){
58-
startAction("runDistributable")
60+
startJob("runDistributable")
5961
}
6062

6163
fun stop(){
62-
stopActions()
64+
stopJobs()
6365
}
6466

65-
private fun startAction(vararg tasks: String) {
67+
private fun startJob(vararg tasks: String) {
6668
if(!active.value) return
67-
editor?.let { println(Language.text("gradle.using_gradle")) }
69+
editor?.let { println(text("gradle.using_gradle")) }
6870

6971
val job = GradleJob()
7072
job.service = this
7173
job.configure = {
72-
setup()
74+
setupGradle()
7375
forTasks(tasks.joinToString(" "))
7476
}
7577
jobs.add(job)
7678
job.start()
7779
}
7880

79-
private fun stopActions(){
80-
jobs
81-
.forEach(GradleJob::cancel)
81+
private fun stopJobs(){
82+
jobs.forEach(GradleJob::cancel)
8283
}
8384

84-
private fun setupGradle(): MutableList<String> {
85+
private fun BuildLauncher.setupGradle(extraArguments: List<String> = listOf()) {
8586
val sketch = sketch ?: throw IllegalStateException("Sketch is not set")
86-
8787
val copy = sketch.isReadOnly || sketch.isUntitled
88-
8988
val sketchFolder = if(copy) workingDir.resolve("sketch").toFile() else sketch.folder
9089
if(copy){
9190
// If the sketch is read-only, we copy it to the working directory
9291
// This allows us to run the sketch without modifying the original files
9392
sketch.folder.copyRecursively(sketchFolder, overwrite = true)
9493
}
95-
94+
// Save the unsaved code into the working directory for gradle to compile
9695
val unsaved = sketch.code
9796
.map { code ->
9897
val file = workingDir.resolve("unsaved/${code.fileName}")
@@ -106,16 +105,18 @@ class GradleService(
106105
}
107106
return@map code.fileName
108107
}
109-
108+
// Collect the variables to pass to gradle
110109
val variables = mapOf(
111110
"group" to System.getProperty("processing.group", "org.processing"),
112-
"version" to Base.getVersionName(),
111+
"version" to getVersionName(),
113112
"sketchFolder" to sketchFolder,
114-
"sketchbook" to Base.getSketchbookFolder(),
113+
"sketchbook" to getSketchbookFolder(),
115114
"workingDir" to workingDir.toAbsolutePath().toString(),
116-
"settings" to Platform.getSettingsFolder().absolutePath.toString(),
115+
"settings" to getSettingsFolder().absolutePath.toString(),
117116
"unsaved" to unsaved.joinToString(","),
118117
"debugPort" to debugPort.toString(),
118+
"logPort" to logPort.toString(),
119+
"errPort" to errPort.toString(),
119120
"fullscreen" to System.getProperty("processing.fullscreen", "false").equals("true"),
120121
"display" to 1, // TODO: Implement
121122
"external" to true,
@@ -126,8 +127,8 @@ class GradleService(
126127
//"stop.color" to "0xFF000000", // TODO: Implement
127128
"stop.hide" to false, // TODO: Implement
128129
)
129-
val repository = Platform.getContentFile("repository").absolutePath.replace("""\""", """\\""")
130-
130+
val repository = getContentFile("repository").absolutePath.replace("""\""", """\\""")
131+
// Create the init.gradle.kts file in the working directory
131132
val initGradle = workingDir.resolve("init.gradle.kts").apply {
132133
val content = """
133134
beforeSettings{
@@ -148,8 +149,7 @@ class GradleService(
148149

149150
writeText(content)
150151
}
151-
152-
152+
// Create the build.gradle.kts file in the sketch folder
153153
val buildGradle = sketchFolder.resolve("build.gradle.kts")
154154
val generate = buildGradle.let {
155155
if(!it.exists()) return@let true
@@ -158,67 +158,63 @@ class GradleService(
158158
if(!contents.contains("@processing-auto-generated")) return@let false
159159

160160
val version = contents.substringAfter("version=").substringBefore("\n")
161-
if(version != Base.getVersionName()) return@let true
161+
if(version != getVersionName()) return@let true
162162

163163
val modeTitle = contents.substringAfter("mode=").substringBefore(" ")
164-
if(this.mode.title != modeTitle) return@let true
164+
if(mode.title != modeTitle) return@let true
165165

166-
return@let Base.DEBUG
166+
return@let DEBUG
167167
}
168168
if (generate) {
169169
Messages.log("build.gradle.kts outdated or not found in ${sketch.folder}, creating one")
170170
val header = """
171-
// @processing-auto-generated mode=${mode.title} version=${Base.getVersionName()}
171+
// @processing-auto-generated mode=${mode.title} version=${getVersionName()}
172172
//
173173
""".trimIndent()
174174

175-
val instructions = Language.text("gradle.instructions")
175+
val instructions = text("gradle.instructions")
176176
.split("\n")
177177
.joinToString("\n") { "// $it" }
178178

179179
val configuration = """
180180
181181
plugins{
182-
id("org.processing.java") version "${Base.getVersionName()}"
182+
id("org.processing.java") version "${getVersionName()}"
183183
}
184184
""".trimIndent()
185185
val content = "${header}\n${instructions}\n${configuration}"
186186
buildGradle.writeText(content)
187187
}
188+
// Create the settings.gradle.kts file in the sketch folder
188189
val settingsGradle = sketchFolder.resolve("settings.gradle.kts")
189190
if (!settingsGradle.exists()) {
190191
settingsGradle.createNewFile()
191192
}
192-
193+
// Collect the arguments to pass to gradle
193194
val arguments = mutableListOf("--init-script", initGradle.toAbsolutePath().toString())
194-
if (!Base.DEBUG) arguments.add("--quiet")
195+
if (!DEBUG) arguments.add("--quiet")
195196
if(copy){
196197
arguments += listOf("--project-dir", sketchFolder.absolutePath)
197198
}
199+
198200
arguments.addAll(variables.entries
199201
.filter { it.value != null }
200202
.map { "-Pprocessing.${it.key}=${it.value}" }
201203
)
204+
arguments.addAll(extraArguments)
202205

203-
return arguments
204-
}
205-
206+
withArguments(*arguments.toTypedArray())
206207

207-
private fun BuildLauncher.setup(extraArguments: List<String> = listOf()) {
208208
// TODO: Instead of shipping Processing with a build-in JDK we should download the JDK through Gradle
209209
setJavaHome(Platform.getJavaHome())
210-
211-
val arguments = setupGradle()
212-
arguments.addAll(extraArguments)
213-
withArguments(*arguments.toTypedArray())
214210
}
215211

216212
// Hooks for java to check if the Gradle service is running since mutableStateOf is not accessible in java
217213
fun getEnabled(): Boolean {
218214
return active.value
219215
}
220216
fun setEnabled(active: Boolean) {
221-
if(!active) stopActions()
217+
if(!active) stopJobs()
222218
this.active.value = active
223219
}
224220
}

java/gradle/src/main/kotlin/ProcessingPlugin.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.jetbrains.compose.ComposeExtension
1313
import org.jetbrains.compose.desktop.DesktopExtension
1414
import processing.app.Preferences
1515
import java.io.File
16+
import java.net.Socket
1617
import java.util.*
1718
import javax.inject.Inject
1819

@@ -26,6 +27,8 @@ class ProcessingPlugin @Inject constructor(private val objectFactory: ObjectFact
2627
val processingGroup = project.findProperty("processing.group") as String? ?: "org.processing"
2728
val workingDir = project.findProperty("processing.workingDir") as String?
2829
val debugPort = project.findProperty("processing.debugPort") as String?
30+
val logPort = project.findProperty("processing.logPort") as String?
31+
val errPort = project.findProperty("processing.errPort") as String?
2932

3033
// TODO: Setup sketchbook when using as a standalone plugin, use the Java Preferences
3134
val sketchbook = project.findProperty("processing.sketchbook") as String?
@@ -120,6 +123,10 @@ class ProcessingPlugin @Inject constructor(private val objectFactory: ObjectFact
120123
project.properties
121124
.filterKeys { it.startsWith("processing") }
122125
.forEach { (key, value) -> task.systemProperty(key, value) }
126+
127+
if(logPort != null) task.standardOutput = Socket("localhost", logPort.toInt()).outputStream
128+
if(errPort != null) task.errorOutput = Socket("localhost", errPort.toInt()).outputStream
129+
123130
}
124131

125132
project.extensions.getByType(JavaPluginExtension::class.java).sourceSets.all { sourceSet ->

0 commit comments

Comments
 (0)