@@ -2,6 +2,10 @@ package processing.app.gradle
22
33import androidx.compose.runtime.mutableStateListOf
44import androidx.compose.runtime.mutableStateOf
5+ import com.sun.jdi.Bootstrap
6+ import com.sun.jdi.VirtualMachine
7+ import com.sun.jdi.VirtualMachineManager
8+ import com.sun.jdi.connect.AttachingConnector
59import kotlinx.coroutines.*
610import org.gradle.tooling.BuildLauncher
711import org.gradle.tooling.GradleConnector
@@ -19,12 +23,12 @@ import javax.swing.event.DocumentEvent
1923import javax.swing.event.DocumentListener
2024import kotlin.io.path.writeText
2125
22- // TODO: Embed the core, gradle plugin, and preprocessor in a custom .m2 repository
23- // Right now the gradle service only works if you publish those to the local maven repository
2426class GradleService (val editor : Editor ) {
2527 val availableTasks = mutableStateListOf<String >()
2628 val finishedTasks = mutableStateListOf<String >()
2729 val running = mutableStateOf(false )
30+ var vm: VirtualMachine ? = null
31+ val debugPort = (30000 .. 60000 ).random()
2832
2933 private var connection: ProjectConnection ? = null
3034 private var preparation: Job ? = null
@@ -35,7 +39,9 @@ class GradleService(val editor: Editor) {
3539
3640 val folder: File get() = editor.sketch.folder
3741
42+
3843 fun prepare (){
44+ Messages .log(" Preparing sketch" )
3945 if (preparing) return
4046 preparation?.cancel()
4147 preparation = CoroutineScope (Dispatchers .IO ).launch {
@@ -44,60 +50,45 @@ class GradleService(val editor: Editor) {
4450 preparing = true
4551
4652 connection.newSketchBuild()
47- .forTasks(" build " )
53+ .forTasks(" jar " )
4854 .run ()
4955
5056 preparing = false
57+ Messages .log(" Preparation finished" )
5158 }
5259 }
5360
5461 fun run (){
5562 val connection = connection ? : return
56- if (! preparing) preparation?.cancel()
63+ Messages .log(" Running sketch" )
64+ startRun {
65+ connection.newSketchBuild()
66+ .addDebugging()
67+ .forTasks(" run" )
68+ .withCancellationToken(cancel.token())
69+ .run ()
70+ Messages .log(" Running finished" )
71+ }
5772
58- run?.cancel()
59- run = CoroutineScope (Dispatchers .IO ).launch {
60- running.value = true
61- preparation?.join()
62- cancel.cancel()
63- cancel = GradleConnector .newCancellationTokenSource()
64- try {
65- connection.newSketchBuild()
66- .forTasks(" run" )
67- .withCancellationToken(cancel.token())
68- .run ()
69- }catch (e: Exception ){
70- Messages .log(e.toString())
71- }
73+ }
74+
75+ fun export (){
76+ val connection = connection ? : return
77+ Messages .log(" Exporting sketch" )
78+ startRun {
79+ connection.newSketchBuild()
80+ .forTasks(" runDistributable" )
81+ .withCancellationToken(cancel.token())
82+ .run ()
83+ Messages .log(" Exporting finished" )
7284 }
73- run?.invokeOnCompletion { running.value = run?.isActive ? : false }
7485 }
7586
7687 fun stop (){
7788 cancel.cancel()
7889 }
7990
80- fun export (){
81- val connection = connection ? : return
82- if (! preparing) preparation?.cancel()
8391
84- run?.cancel()
85- run = CoroutineScope (Dispatchers .IO ).launch {
86- running.value = true
87- preparation?.join()
88- cancel.cancel()
89- cancel = GradleConnector .newCancellationTokenSource()
90- try {
91- connection.newSketchBuild()
92- .forTasks(" runDistributable" )
93- .withCancellationToken(cancel.token())
94- .run ()
95- }catch (e: Exception ){
96- Messages .log(e.toString())
97- }
98- }
99- run?.invokeOnCompletion { running.value = run?.isActive ? : false }
100- }
10192
10293 fun startService (){
10394 Messages .log(" Starting Gradle service at ${folder} " )
@@ -108,8 +99,6 @@ class GradleService(val editor: Editor) {
10899
109100 // TODO: recreate connection if sketch folder changes
110101
111- // TODO: Run the sketch with the latest changes
112-
113102 SwingUtilities .invokeLater {
114103 editor.sketch.code.forEach {
115104 it.document.addDocumentListener(object : DocumentListener {
@@ -129,11 +118,37 @@ class GradleService(val editor: Editor) {
129118
130119 // TODO: Attach listener if new tab is created
131120 }
132-
133- // TODO: Attach a debugger (to gradle, and the running sketch)
134121 }
135122
123+ private fun startRun (action : () -> Unit ){
124+ running.value = true
125+ run?.cancel()
126+ run = CoroutineScope (Dispatchers .IO ).launch {
127+ preparation?.join()
128+
129+ cancel.cancel()
130+ cancel = GradleConnector .newCancellationTokenSource()
131+ action()
132+ }
133+ run?.invokeOnCompletion { running.value = run?.isActive ? : false }
134+ }
136135
136+ private fun BuildLauncher.addDebugging (): BuildLauncher {
137+ this .addProgressListener(ProgressListener { event ->
138+ if (event !is TaskStartEvent ) return @ProgressListener
139+ if (event.descriptor.name != " :run" ) return @ProgressListener
140+
141+ Messages .log(" Running sketch" )
142+ val connector = Bootstrap .virtualMachineManager().allConnectors()
143+ .firstOrNull { it.name() == " com.sun.jdi.SocketAttach" }
144+ as AttachingConnector ?
145+ ? : return @ProgressListener
146+ val args = connector.defaultArguments()
147+ args[" port" ]?.setValue(debugPort.toString())
148+ vm = connector.attach(args)
149+ })
150+ return this
151+ }
137152
138153
139154 private fun ProjectConnection.newSketchBuild (): BuildLauncher {
@@ -160,7 +175,8 @@ class GradleService(val editor: Editor) {
160175 " sketchFolder" to folder.absolutePath,
161176 " workingDir" to workingDir.toAbsolutePath().toString(),
162177 " settings" to Platform .getSettingsFolder().absolutePath.toString(),
163- " unsaved" to unsaved.joinToString(" ," )
178+ " unsaved" to unsaved.joinToString(" ," ),
179+ " debugPort" to debugPort.toString()
164180 )
165181 val repository = Platform .getContentFile(" repository" ).absolutePath
166182
@@ -204,9 +220,13 @@ class GradleService(val editor: Editor) {
204220 """ .trimIndent()
205221 buildGradle.writeText(content)
206222 }
223+ val settingsGradle = folder.resolve(" settings.gradle.kts" )
224+ if (! settingsGradle.exists()) {
225+ settingsGradle.createNewFile()
226+ }
207227
208228 return this .newBuild()
209- .setJavaHome(Platform .getJavaHome())
229+ // .setJavaHome(Platform.getJavaHome())
210230 .withArguments(
211231 " --init-script" , initGradle.toAbsolutePath().toString(),
212232 * variables.entries.map { " -Pprocessing.${it.key} =${it.value} " }.toTypedArray()
0 commit comments