1+ package processing.app.gradle
2+
3+ import androidx.compose.runtime.mutableStateListOf
4+ import androidx.compose.runtime.mutableStateOf
5+ import com.sun.jdi.Bootstrap
6+ import com.sun.jdi.VirtualMachine
7+ import com.sun.jdi.connect.AttachingConnector
8+ import kotlinx.coroutines.CoroutineScope
9+ import kotlinx.coroutines.Dispatchers
10+ import kotlinx.coroutines.launch
11+ import org.gradle.tooling.BuildLauncher
12+ import org.gradle.tooling.GradleConnector
13+ import org.gradle.tooling.events.ProgressListener
14+ import org.gradle.tooling.events.problems.ProblemEvent
15+ import org.gradle.tooling.events.task.TaskFinishEvent
16+ import org.gradle.tooling.events.task.TaskStartEvent
17+ import processing.app.Messages
18+
19+ // TODO: Capture and filter gradle output
20+ open abstract class GradleJob {
21+ enum class State {
22+ NONE ,
23+ BUILDING ,
24+ RUNNING ,
25+ DONE
26+ }
27+
28+ var service: GradleService ? = null
29+ var configure: BuildLauncher .() -> Unit = {}
30+
31+ val state = mutableStateOf(State .NONE )
32+ val vm = mutableStateOf<VirtualMachine ?>(null )
33+ val problems = mutableStateListOf<ProblemEvent >()
34+
35+ private val scope = CoroutineScope (Dispatchers .IO )
36+ private val cancel = GradleConnector .newCancellationTokenSource()
37+
38+ fun start () {
39+ service?.jobs?.add(this )
40+ val connection = service?.connection ? : return
41+ scope.launch {
42+ try {
43+ state.value = State .BUILDING
44+
45+ connection.newBuild()
46+ .apply {
47+ configure()
48+ }
49+ .withCancellationToken(cancel.token())
50+ .addStateListener()
51+ .addDebugging()
52+ .run ()
53+
54+ }catch (e: Exception ){
55+ if (state.value == State .RUNNING ){
56+ Messages .log(" Error while running: ${e.message} " )
57+ }else {
58+ throw e
59+ }
60+ }finally {
61+ state.value = State .DONE
62+ vm.value = null
63+ }
64+
65+ }
66+ }
67+
68+ fun cancel (){
69+ cancel.cancel()
70+ }
71+
72+ private fun BuildLauncher.addStateListener (): BuildLauncher {
73+ this .addProgressListener(ProgressListener { event ->
74+ if (event is TaskStartEvent ) {
75+ when (event.descriptor.name) {
76+ " :run" -> {
77+ state.value = State .RUNNING
78+ }
79+ }
80+
81+ }
82+ if (event is TaskFinishEvent ) {
83+ when (event.descriptor.name){
84+ " :jar" -> {
85+ state.value = State .NONE
86+ Messages .log(" Jar finished" )
87+ }
88+ " :run" -> {
89+ state.value = State .NONE
90+ }
91+ }
92+ }
93+ if (event is ProblemEvent ) {
94+ problems.add(event)
95+ }
96+ })
97+ return this
98+ }
99+
100+ private fun BuildLauncher.addDebugging (): BuildLauncher {
101+ this .addProgressListener(ProgressListener { event ->
102+ if (event !is TaskStartEvent ) return @ProgressListener
103+ if (event.descriptor.name != " :run" ) return @ProgressListener
104+
105+ Messages .log(" Attaching to VM" )
106+ val connector = Bootstrap .virtualMachineManager().allConnectors()
107+ .firstOrNull { it.name() == " com.sun.jdi.SocketAttach" }
108+ as AttachingConnector ?
109+ ? : return @ProgressListener
110+ val args = connector.defaultArguments()
111+ args[" port" ]?.setValue(service?.debugPort.toString())
112+ val sketch = connector.attach(args)
113+ vm.value = sketch
114+ Messages .log(" Attached to VM: ${sketch.name()} " )
115+ })
116+ return this
117+ }
118+ }
0 commit comments