// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.13.2' } } allprojects { repositories { google() mavenCentral() maven { url 'https://jitpack.io' } } } task clean(type: Delete) { delete rootProject.buildDir } def loadLocalProperties() { def props = new Properties() def propsFile = rootProject.file("local.properties") if (propsFile.exists()) { propsFile.withInputStream { props.load(it) } } return props } def resolveSdkDir() { def props = loadLocalProperties() return props.getProperty("sdk.dir") ?: System.getenv("ANDROID_SDK_ROOT") ?: System.getenv("ANDROID_HOME") } def isWindows = System.getProperty("os.name").toLowerCase().contains("windows") def npmCmd = isWindows ? "npm.cmd" : "npm" def findWindowsNpm() { def candidates = [] def pathEnv = System.getenv("PATH") ?: "" pathEnv.split(";").each { p -> if (!p) return def cmd = new File(p, "npm.cmd") if (cmd.exists()) candidates << cmd def exe = new File(p, "npm.exe") if (exe.exists()) candidates << exe } def nvmHome = System.getenv("NVM_HOME") def nvmSymlink = System.getenv("NVM_SYMLINK") if (nvmSymlink) { def cmd = new File(nvmSymlink, "npm.cmd") if (cmd.exists()) candidates << cmd } if (nvmHome) { def current = new File(nvmHome, "current") if (current.exists()) { def cmd = new File(current, "npm.cmd") if (cmd.exists()) candidates << cmd } def versions = new File(nvmHome) if (versions.exists()) { versions.listFiles()?.findAll { it.isDirectory() }?.each { v -> def cmd = new File(v, "npm.cmd") if (cmd.exists()) candidates << cmd } } } def appData = System.getenv("APPDATA") if (appData) { def cmd = new File(appData, "npm\\npm.cmd") if (cmd.exists()) candidates << cmd } def programFiles = System.getenv("ProgramFiles") if (programFiles) { def cmd = new File(programFiles, "nodejs\\npm.cmd") if (cmd.exists()) candidates << cmd } def localAppData = System.getenv("LOCALAPPDATA") if (localAppData) { def nvmLocal = new File(localAppData, "nvm") if (nvmLocal.exists()) { nvmLocal.listFiles()?.findAll { it.isDirectory() }?.each { v -> def cmd = new File(v, "npm.cmd") if (cmd.exists()) candidates << cmd } } } return candidates.isEmpty() ? null : candidates.first().absolutePath } def findWindowsNode() { def candidates = [] def pathEnv = System.getenv("PATH") ?: "" pathEnv.split(";").each { p -> if (!p) return def exe = new File(p, "node.exe") if (exe.exists()) candidates << exe } def nvmHome = System.getenv("NVM_HOME") def nvmSymlink = System.getenv("NVM_SYMLINK") if (nvmSymlink) { def exe = new File(nvmSymlink, "node.exe") if (exe.exists()) candidates << exe } if (nvmHome) { def current = new File(nvmHome, "current") if (current.exists()) { def exe = new File(current, "node.exe") if (exe.exists()) candidates << exe } def versions = new File(nvmHome) if (versions.exists()) { versions.listFiles()?.findAll { it.isDirectory() }?.each { v -> def exe = new File(v, "node.exe") if (exe.exists()) candidates << exe } } } def programFiles = System.getenv("ProgramFiles") if (programFiles) { def exe = new File(programFiles, "nodejs\\node.exe") if (exe.exists()) candidates << exe } def localAppData = System.getenv("LOCALAPPDATA") if (localAppData) { def nvmLocal = new File(localAppData, "nvm") if (nvmLocal.exists()) { nvmLocal.listFiles()?.findAll { it.isDirectory() }?.each { v -> def exe = new File(v, "node.exe") if (exe.exists()) candidates << exe } } } return candidates.isEmpty() ? null : candidates.first().absolutePath } def backendDir = file("${rootDir}/backend") def backendNodeModulesDir = new File(backendDir, "node_modules") def npmPathProp = providers.gradleProperty("npmPath") .orElse(providers.environmentVariable("NPM_PATH")) def resolvedWindowsNpm = null def resolvedWindowsNode = null if (isWindows) { resolvedWindowsNpm = npmPathProp.orNull?.toString() ?: findWindowsNpm() if (!resolvedWindowsNpm) { throw new GradleException("npm not found. Set -PnpmPath=... or ensure npm is visible to cmd.exe. If using nvm, try your NVM_HOME version's npm.cmd.") } resolvedWindowsNode = findWindowsNode() if (!resolvedWindowsNode) { throw new GradleException("node.exe not found. Ensure Node.js is installed and visible to cmd.exe.") } } tasks.register("installBackendDeps", Exec) { workingDir backendDir onlyIf { !backendNodeModulesDir.exists() } if (isWindows) { commandLine "cmd", "/c", resolvedWindowsNpm, "install" } else { commandLine "sh", "-c", "${npmCmd} install" } } tasks.register("startBackend") { dependsOn("installBackendDeps") doLast { def command = isWindows ? [resolvedWindowsNode, "--no-deprecation", "server.js"] : ["sh", "-c", "${npmCmd} start"] def process = new ProcessBuilder(command) .directory(backendDir) .start() def stopProcess = { if (!process.isAlive()) { return } if (isWindows) { new ProcessBuilder("cmd", "/c", "taskkill", "/PID", process.pid().toString(), "/T", "/F") .start() .waitFor() } else { process.destroy() process.waitFor(5, java.util.concurrent.TimeUnit.SECONDS) if (process.isAlive()) { process.destroyForcibly() } } } def pumpStream = { input, output -> Thread.start { input.withReader("UTF-8") { reader -> String line while ((line = reader.readLine()) != null) { output.println(line) } } } } def outThread = pumpStream(process.inputStream, System.out) def errThread = pumpStream(process.errorStream, System.err) def shutdownHook = new Thread({ stopProcess() }, "stop-backend-on-gradle-exit") Runtime.getRuntime().addShutdownHook(shutdownHook) try { def exitCode = process.waitFor() outThread.join(1000) errThread.join(1000) if (exitCode != 0) { throw new GradleException("Backend process exited with code ${exitCode}") } } finally { stopProcess() try { Runtime.getRuntime().removeShutdownHook(shutdownHook) } catch (IllegalStateException ignored) { // Ignore; JVM is already shutting down and hook execution has begun. } } } } tasks.register("runBackendDebug") { dependsOn(":app:installDebug", "startBackend") doLast { def sdkDir = resolveSdkDir() if (!sdkDir) { throw new GradleException("Missing sdk.dir in local.properties and ANDROID_SDK_ROOT/ANDROID_HOME is not set") } def adb = "${sdkDir}/platform-tools/adb" + (isWindows ? ".exe" : "") exec { commandLine adb, "shell", "am", "start", "-n", "com.dark98.santoku/.MainActivity" } } }