@@ -26,18 +26,24 @@ import org.gradle.api.file.DirectoryProperty
2626import org.gradle.api.logging.Logger
2727import org.gradle.api.logging.Logging
2828import org.gradle.api.provider.Property
29+ import org.gradle.api.provider.ProviderFactory
2930import org.gradle.api.services.BuildService
3031import org.gradle.api.services.BuildServiceParameters
32+ import org.gradle.jvm.toolchain.JavaLauncher
33+ import org.gradle.process.ExecOperations
3134import xyz.jpenilla.runtask.paperapi.DownloadsAPI
3235import xyz.jpenilla.runtask.util.Constants
3336import xyz.jpenilla.runtask.util.Downloader
3437import xyz.jpenilla.runtask.util.InvalidDurationException
38+ import xyz.jpenilla.runtask.util.maybeApplyPaperclip
3539import xyz.jpenilla.runtask.util.parseDuration
3640import xyz.jpenilla.runtask.util.path
3741import xyz.jpenilla.runtask.util.prettyPrint
3842import xyz.jpenilla.runtask.util.sha256
43+ import xyz.jpenilla.runtask.util.walkMatching
3944import java.io.IOException
4045import java.net.URL
46+ import java.nio.file.Files
4147import java.nio.file.Path
4248import java.nio.file.StandardCopyOption
4349import java.time.Duration
@@ -96,11 +102,15 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
96102 val removed = jars.remove(oldestBuild) ? : error(" Build does not exist?" )
97103 version.knownJars.remove(oldestBuild)
98104
99- val oldJar = jarsFor(versionName).resolve(removed.fileName)
100- try {
101- oldJar.deleteIfExists()
102- } catch (ex: IOException ) {
103- LOGGER .warn(" Failed to delete jar at {}" , oldJar.absolutePathString(), ex)
105+ val toDelete = removed.fileName?.let { listOf (it) }
106+ ? : requireNotNull(removed.classpath).toList()
107+ for (path in toDelete) {
108+ val delete = jarsFor(versionName).resolve(path)
109+ try {
110+ delete.deleteIfExists()
111+ } catch (ex: IOException ) {
112+ LOGGER .warn(" Failed to delete jar at {}" , delete.absolutePathString(), ex)
113+ }
104114 }
105115 writeVersions()
106116 }
@@ -119,29 +129,69 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
119129 @Synchronized
120130 override fun resolveBuild (
121131 project : Project ,
132+ providers : ProviderFactory ,
133+ javaLauncher : JavaLauncher ,
134+ execOperations : ExecOperations ,
122135 version : String ,
123136 build : DownloadsAPIService .Build
124- ): Path {
137+ ): List < Path > {
125138 versions = loadOrCreateVersions()
126139
127140 val versionData = versions.versions.computeIfAbsent(version) { Version (it) }
128- val buildNumber = resolveBuildNumber(project, versionData, build)
141+ val buildNumber = resolveBuildNumber(providers, versionData, build)
142+ val jarsDir = jarsFor(version)
129143
130144 val possible = versionData.knownJars[buildNumber]
131145 if (possible != null && ! parameters.refreshDependencies.get()) {
132146 // We already have this jar!
133147 LOGGER .lifecycle(" Located {} {} build {} in local cache." , displayName, version, buildNumber)
134148
149+ if (possible.classpath != null && possible.classpath.isNotEmpty()) {
150+ return possible.classpath.flatMap { jarsDir.walkMatching(it) }
151+ }
152+
153+ requireNotNull(possible.fileName)
154+ requireNotNull(possible.sha256)
155+
135156 // Verify hash is still correct
136- val localJar = jarsFor(version) .resolve(possible.fileName)
157+ val localJar = jarsDir .resolve(possible.fileName)
137158 val localBuildHash = localJar.sha256()
138159 if (localBuildHash == possible.sha256) {
139160 if (build is DownloadsAPIService .Build .Specific ) {
140161 versionData.knownJars[buildNumber] = possible.copy(keep = true )
141162 writeVersions()
142163 }
143164 // Hash is good, return
144- return localJar
165+ if (possible.classpath == null ) {
166+ val workDir = jarsDir.resolve(buildNumber.toString()).createDirectories()
167+ val classpath = maybeApplyPaperclip(
168+ javaLauncher,
169+ execOperations,
170+ localJar,
171+ workDir,
172+ jarsDir,
173+ )
174+
175+ if (classpath != null ) {
176+ localJar.deleteIfExists()
177+ versionData.knownJars[buildNumber] = possible.copy(
178+ fileName = null ,
179+ sha256 = null ,
180+ classpath = classpath
181+ )
182+ writeVersions()
183+ return classpath.flatMap { jarsDir.walkMatching(it) }
184+ } else {
185+ Files .walk(workDir).use { stream ->
186+ stream.sorted(Comparator .reverseOrder()).forEach { Files .deleteIfExists(it) }
187+ }
188+ versionData.knownJars[buildNumber] = possible.copy(classpath = emptyList())
189+ writeVersions()
190+ return listOf (localJar)
191+ }
192+ } else if (possible.classpath.isEmpty()) {
193+ return listOf (localJar)
194+ }
145195 }
146196 versionData.knownJars.remove(buildNumber)
147197 writeVersions()
@@ -181,27 +231,40 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
181231 }
182232 LOGGER .lifecycle(" Verified SHA256 hash of downloaded jar." )
183233
184- val jarsDir = jarsFor(version)
185234 jarsDir.createDirectories()
186235 val fileName = " $buildNumber .jar"
187236 val destination = jarsDir.resolve(fileName)
188237
189- tempFile.moveTo(destination, StandardCopyOption .REPLACE_EXISTING )
238+ val classpath = maybeApplyPaperclip(
239+ javaLauncher,
240+ execOperations,
241+ tempFile,
242+ jarsDir.resolve(buildNumber.toString()),
243+ jarsDir,
244+ )
245+
246+ if (classpath != null ) {
247+ tempFile.deleteIfExists()
248+ } else {
249+ tempFile.moveTo(destination, StandardCopyOption .REPLACE_EXISTING )
250+ }
190251
191252 versionData.knownJars[buildNumber] = JarInfo (
192253 buildNumber,
193- fileName,
194- download.sha256,
254+ if (classpath == null ) fileName else null ,
255+ if (classpath == null ) download.sha256 else null ,
256+ classpath,
195257 // If the build was specifically requested, (as opposed to resolved as latest) mark the jar for keeping
196258 build is DownloadsAPIService .Build .Specific
197259 )
198260 writeVersions()
199261
200- return destination
262+ return classpath?.flatMap { jarsDir.walkMatching(it) }
263+ ? : listOf (destination)
201264 }
202265
203266 private fun resolveBuildNumber (
204- project : Project ,
267+ providers : ProviderFactory ,
205268 version : Version ,
206269 build : DownloadsAPIService .Build
207270 ): Int {
@@ -215,7 +278,7 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
215278 }
216279
217280 if (! parameters.refreshDependencies.get()) {
218- val checkFrequency = updateCheckFrequency(project )
281+ val checkFrequency = updateCheckFrequency(providers )
219282 val timeSinceLastCheck = System .currentTimeMillis() - version.lastUpdateCheck
220283 if (timeSinceLastCheck <= checkFrequency.toMillis()) {
221284 return resolveLatestLocalBuild(version)
@@ -238,7 +301,7 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
238301 writeVersions()
239302 }
240303 } catch (ex: Exception ) {
241- LOGGER .lifecycle(" Failed to check for latest release, attempting to use latest local build." )
304+ LOGGER .lifecycle(" Failed to check for latest release, attempting to use latest local build." , ex )
242305 resolveLatestLocalBuild(version)
243306 }
244307
@@ -247,10 +310,10 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
247310 LOGGER .lifecycle(" > Actual: {}" , actual)
248311 }
249312
250- private fun updateCheckFrequency (project : Project ): Duration {
251- var prop = project.findProperty (Constants .Properties .UPDATE_CHECK_FREQUENCY )
313+ private fun updateCheckFrequency (providers : ProviderFactory ): Duration {
314+ var prop = providers.gradleProperty (Constants .Properties .UPDATE_CHECK_FREQUENCY ).orNull
252315 if (prop == null ) {
253- prop = project.findProperty (Constants .Properties .UPDATE_CHECK_FREQUENCY_LEGACY )
316+ prop = providers.gradleProperty (Constants .Properties .UPDATE_CHECK_FREQUENCY_LEGACY ).orNull
254317 if (prop != null ) {
255318 LOGGER .warn(
256319 " Use of legacy '{}' property detected. Please replace with '{}'." ,
@@ -263,7 +326,7 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
263326 return Duration .ofHours(1 ) // default to 1 hour if unset
264327 }
265328 try {
266- return parseDuration(prop as String )
329+ return parseDuration(prop)
267330 } catch (ex: InvalidDurationException ) {
268331 throw InvalidUserDataException (" Unable to parse value for property '${Constants .Properties .UPDATE_CHECK_FREQUENCY } '.\n ${ex.message} " , ex)
269332 }
@@ -290,8 +353,9 @@ internal abstract class DownloadsAPIServiceImpl : BuildService<DownloadsAPIServi
290353
291354 private data class JarInfo (
292355 val buildNumber : Int ,
293- val fileName : String ,
294- val sha256 : String ,
356+ val fileName : String? ,
357+ val sha256 : String? ,
358+ val classpath : List <String >? ,
295359 val keep : Boolean = false
296360 )
297361
0 commit comments