From d076c4e73df7bde4ff7ac41dc645e5fd6373fb63 Mon Sep 17 00:00:00 2001 From: Skylot Date: Fri, 7 Jul 2023 22:55:45 +0100 Subject: [PATCH] build: migrate to kotlin dsl in gradle scripts, update gradle and deps --- build.gradle | 183 ------------------ build.gradle.kts | 156 +++++++++++++++ buildSrc/build.gradle | 11 -- buildSrc/build.gradle.kts | 11 ++ buildSrc/src/main/groovy/jadx-kotlin.gradle | 12 -- buildSrc/src/main/groovy/jadx-library.gradle | 79 -------- buildSrc/src/main/kotlin/jadx-java.gradle.kts | 51 +++++ .../src/main/kotlin/jadx-kotlin.gradle.kts | 12 ++ .../src/main/kotlin/jadx-library.gradle.kts | 81 ++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 62076 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 5 +- gradlew | 5 +- jadx-cli/build.gradle | 44 ----- jadx-cli/build.gradle.kts | 39 ++++ jadx-core/build.gradle | 28 --- jadx-core/build.gradle.kts | 33 ++++ jadx-gui/build.gradle | 166 ---------------- jadx-gui/build.gradle.kts | 164 ++++++++++++++++ jadx-plugins/jadx-dex-input/build.gradle | 20 -- jadx-plugins/jadx-dex-input/build.gradle.kts | 20 ++ jadx-plugins/jadx-input-api/build.gradle | 3 - jadx-plugins/jadx-input-api/build.gradle.kts | 3 + jadx-plugins/jadx-java-convert/build.gradle | 13 -- .../jadx-java-convert/build.gradle.kts | 13 ++ .../{build.gradle => build.gradle.kts} | 4 +- .../{build.gradle => build.gradle.kts} | 4 +- .../{build.gradle => build.gradle.kts} | 13 +- .../jadx-script/examples/build.gradle.kts | 2 +- .../jadx-script-plugin/build.gradle.kts | 6 +- .../jadx-script-runtime/build.gradle.kts | 1 - jadx-plugins/jadx-smali-input/build.gradle | 18 -- .../jadx-smali-input/build.gradle.kts | 18 ++ 32 files changed, 623 insertions(+), 595 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 buildSrc/build.gradle create mode 100644 buildSrc/build.gradle.kts delete mode 100644 buildSrc/src/main/groovy/jadx-kotlin.gradle delete mode 100644 buildSrc/src/main/groovy/jadx-library.gradle create mode 100644 buildSrc/src/main/kotlin/jadx-java.gradle.kts create mode 100644 buildSrc/src/main/kotlin/jadx-kotlin.gradle.kts create mode 100644 buildSrc/src/main/kotlin/jadx-library.gradle.kts delete mode 100644 jadx-cli/build.gradle create mode 100644 jadx-cli/build.gradle.kts delete mode 100644 jadx-core/build.gradle create mode 100644 jadx-core/build.gradle.kts delete mode 100644 jadx-gui/build.gradle create mode 100644 jadx-gui/build.gradle.kts delete mode 100644 jadx-plugins/jadx-dex-input/build.gradle create mode 100644 jadx-plugins/jadx-dex-input/build.gradle.kts delete mode 100644 jadx-plugins/jadx-input-api/build.gradle create mode 100644 jadx-plugins/jadx-input-api/build.gradle.kts delete mode 100644 jadx-plugins/jadx-java-convert/build.gradle create mode 100644 jadx-plugins/jadx-java-convert/build.gradle.kts rename jadx-plugins/jadx-java-input/{build.gradle => build.gradle.kts} (63%) rename jadx-plugins/jadx-raung-input/{build.gradle => build.gradle.kts} (62%) rename jadx-plugins/jadx-rename-mappings/{build.gradle => build.gradle.kts} (52%) delete mode 100644 jadx-plugins/jadx-smali-input/build.gradle create mode 100644 jadx-plugins/jadx-smali-input/build.gradle.kts diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 1ff8d4554..000000000 --- a/build.gradle +++ /dev/null @@ -1,183 +0,0 @@ -//file:noinspection UnnecessaryQualifiedReference - -plugins { - id 'com.github.ben-manes.versions' version '0.47.0' - id 'se.patrikerdes.use-latest-versions' version '0.2.18' - id 'com.diffplug.spotless' version '6.19.0' -} - -ext.jadxVersion = System.getenv('JADX_VERSION') ?: "dev" -version = jadxVersion -println("jadx version: ${jadxVersion}") - -allprojects { - apply plugin: 'java' - apply plugin: 'checkstyle' - apply plugin: 'com.diffplug.spotless' - apply plugin: 'com.github.ben-manes.versions' - apply plugin: 'se.patrikerdes.use-latest-versions' - - version = jadxVersion - - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - - compileJava { - options.encoding = "UTF-8" - } - - jar { - manifest { - mainAttributes('jadx-version': jadxVersion) - } - } - - dependencies { - implementation 'org.slf4j:slf4j-api:2.0.7' - compileOnly 'org.jetbrains:annotations:24.0.1' - - testImplementation 'ch.qos.logback:logback-classic:1.4.8' - testImplementation 'org.hamcrest:hamcrest-library:2.2' - testImplementation 'org.mockito:mockito-core:5.4.0' - testImplementation 'org.assertj:assertj-core:3.24.2' - - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.3' - - testCompileOnly 'org.jetbrains:annotations:24.0.1' - } - - test { - useJUnitPlatform() - maxParallelForks = Runtime.runtime.availableProcessors() - } - - repositories { - mavenLocal() - mavenCentral() - google() - } - - spotless { - java { - importOrderFile "$rootDir/config/code-formatter/eclipse.importorder" - eclipse().configFile "$rootDir/config/code-formatter/eclipse.xml" - removeUnusedImports() - - lineEndings(com.diffplug.spotless.LineEnding.UNIX) - encoding("UTF-8") - trimTrailingWhitespace() - endWithNewline() - } - kotlin { - ktlint() - .setUseExperimental(false) - .editorConfigOverride([indent_style: "tab"]) - - lineEndings(com.diffplug.spotless.LineEnding.UNIX) - encoding("UTF-8") - trimTrailingWhitespace() - endWithNewline() - } - format 'misc', { - target '**/*.gradle', '**/*.xml', '**/.gitignore', '**/.properties' - targetExclude ".gradle/**", ".idea/**", "*/build/**" - - lineEndings(com.diffplug.spotless.LineEnding.UNIX) - encoding("UTF-8") - trimTrailingWhitespace() - endWithNewline() - } - } - - dependencyUpdates { - resolutionStrategy { - componentSelection { rules -> - rules.all { ComponentSelection selection -> - boolean rejected = ['alpha', 'beta', 'dev', 'rc', 'cr', 'm', 'atlassian'].any { qualifier -> - selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/ - } - if (rejected) { - selection.reject('Release candidate') - } - } - } - } - } -} - -task copyArtifacts(type: Copy) { - from(tasks.getByPath(":jadx-cli:installShadowDist")) { - exclude '**/*.jar' - filter { line -> - line.replaceAll('jadx-cli-(.*)-all.jar', 'jadx-$1-all.jar') - .replace('-jar "\\"$CLASSPATH\\""', '-cp "\\"$CLASSPATH\\"" jadx.cli.JadxCLI') - .replace('-jar "%CLASSPATH%"', '-cp "%CLASSPATH%" jadx.cli.JadxCLI') - } - } - from(tasks.getByPath(":jadx-gui:installShadowDist")) { - exclude '**/*.jar' - filter { line -> line.replaceAll('jadx-gui-(.*)-all.jar', 'jadx-$1-all.jar') } - } - from(tasks.getByPath(":jadx-gui:installShadowDist")) { - include '**/*.jar' - rename 'jadx-gui-(.*)-all.jar', 'jadx-$1-all.jar' - } - into layout.buildDirectory.dir("jadx") - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} - -task pack(type: Zip) { - from copyArtifacts - archiveFileName = "jadx-${jadxVersion}.zip" - destinationDirectory = layout.buildDirectory -} - -task copyExe(type: Copy) { - group 'jadx' - description = 'Copy exe to build dir' - - // next task dependencies not needed, but gradle throws warning because of same output dir - mustRunAfter jar - mustRunAfter pack - - from tasks.getByPath('jadx-gui:createExe') - include '*.exe' - into layout.buildDirectory - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} - -task distWinBundle(type: Copy, dependsOn: 'jadx-gui:distWinWithJre') { - group 'jadx' - description = 'Copy bundle to build dir' - mustRunAfter pack - destinationDir buildDir - from(tasks.getByPath('jadx-gui:distWinWithJre').outputs) { - include '*.zip' - } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} - -task dist { - group 'jadx' - description = 'Build jadx distribution zip' - - dependsOn(pack) - - OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem; - if (os.isWindows()) { - if (project.hasProperty("bundleJRE")) { - println("Build win bundle with JRE") - dependsOn('distWinBundle') - } else { - dependsOn('copyExe') - } - } -} - -task cleanBuildDir(type: Delete) { - group 'jadx' - delete buildDir -} - -clean.dependsOn(cleanBuildDir) diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..313ce7345 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,156 @@ +import com.diffplug.gradle.spotless.FormatExtension +import com.diffplug.gradle.spotless.SpotlessExtension +import com.diffplug.spotless.LineEnding +import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask +import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform +import java.util.Locale + +plugins { + id("com.github.ben-manes.versions") version "0.47.0" + id("se.patrikerdes.use-latest-versions") version "0.2.18" + id("com.diffplug.spotless") version "6.19.0" +} + +val jadxVersion by extra { System.getenv("JADX_VERSION") ?: "dev" } +println("jadx version: $jadxVersion") +version = jadxVersion + +allprojects { + apply(plugin = "java") + apply(plugin = "checkstyle") + apply(plugin = "com.diffplug.spotless") + apply(plugin = "com.github.ben-manes.versions") + apply(plugin = "se.patrikerdes.use-latest-versions") + + repositories { + mavenCentral() + } + + configure { + java { + importOrderFile("$rootDir/config/code-formatter/eclipse.importorder") + eclipse().configFile("$rootDir/config/code-formatter/eclipse.xml") + removeUnusedImports() + commonFormatOptions() + } + kotlin { + ktlint() + .setUseExperimental(false) + .editorConfigOverride(mapOf("indent_style" to "tab")) + commonFormatOptions() + } + kotlinGradle { + ktlint() + commonFormatOptions() + } + format("misc") { + target("**/*.gradle", "**/*.xml", "**/.gitignore", "**/.properties") + targetExclude(".gradle/**", ".idea/**", "*/build/**") + commonFormatOptions() + } + } + + tasks.named("dependencyUpdates") { + rejectVersionIf { + // disallow release candidates as upgradable versions from stable versions + isNonStable(candidate.version) && !isNonStable(currentVersion) + } + } +} + +fun FormatExtension.commonFormatOptions() { + lineEndings = LineEnding.UNIX + encoding = Charsets.UTF_8 + trimTrailingWhitespace() + endWithNewline() +} + +fun isNonStable(version: String): Boolean { + val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase(Locale.getDefault()).contains(it) } + val regex = "^[0-9,.v-]+(-r)?$".toRegex() + val isStable = stableKeyword || regex.matches(version) + return isStable.not() +} + +val copyArtifacts by tasks.registering(Copy::class) { + val jarCliPattern = "jadx-cli-(.*)-all.jar".toPattern() + from(tasks.getByPath(":jadx-cli:installShadowDist")) { + exclude("**/*.jar") + filter { line -> + jarCliPattern.matcher(line).replaceAll("jadx-$1-all.jar") + .replace("-jar \"\\\"\$CLASSPATH\\\"\"", "-cp \"\\\"\$CLASSPATH\\\"\" jadx.cli.JadxCLI") + .replace("-jar \"%CLASSPATH%\"", "-cp \"%CLASSPATH%\" jadx.cli.JadxCLI") + } + } + val jarGuiPattern = "jadx-gui-(.*)-all.jar".toPattern() + from(tasks.getByPath(":jadx-gui:installShadowDist")) { + exclude("**/*.jar") + filter { line -> jarGuiPattern.matcher(line).replaceAll("jadx-$1-all.jar") } + } + from(tasks.getByPath(":jadx-gui:installShadowDist")) { + include("**/*.jar") + rename("jadx-gui-(.*)-all.jar", "jadx-$1-all.jar") + } + into(layout.buildDirectory.dir("jadx")) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +val pack by tasks.registering(Zip::class) { + from(copyArtifacts) + archiveFileName.set("jadx-$jadxVersion.zip") + destinationDirectory.set(layout.buildDirectory) +} + +val copyExe by tasks.registering(Copy::class) { + group = "jadx" + description = "Copy exe to build dir" + + // next task dependencies not needed, but gradle throws warning because of same output dir + mustRunAfter("jar") + mustRunAfter(pack) + + from(tasks.getByPath("jadx-gui:createExe")) + include("*.exe") + into(layout.buildDirectory) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +val distWinBundle by tasks.registering(Copy::class) { + group = "jadx" + description = "Copy bundle to build dir" + + dependsOn(tasks.getByPath(":jadx-gui:distWinWithJre")) + + // next task dependencies not needed, but gradle throws warning because of same output dir + mustRunAfter("jar") + mustRunAfter(pack) + + from(tasks.getByPath("jadx-gui:distWinWithJre").outputs) { + include("*.zip") + } + into(layout.buildDirectory) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +val dist by tasks.registering { + group = "jadx" + description = "Build jadx distribution zip" + + dependsOn(pack) + + val os = DefaultNativePlatform.getCurrentOperatingSystem() + if (os.isWindows) { + if (project.hasProperty("bundleJRE")) { + println("Build win bundle with JRE") + dependsOn(distWinBundle) + } else { + dependsOn(copyExe) + } + } +} + +val cleanBuildDir by tasks.registering(Delete::class) { + group = "jadx" + delete(buildDir) +} +tasks.getByName("clean").dependsOn(cleanBuildDir) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle deleted file mode 100644 index 08c73e76d..000000000 --- a/buildSrc/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -plugins { - id 'groovy-gradle-plugin' -} - -dependencies { - implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22' -} - -repositories { - gradlePluginPortal() -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..2a0e49279 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + `kotlin-dsl` +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") +} + +repositories { + gradlePluginPortal() +} diff --git a/buildSrc/src/main/groovy/jadx-kotlin.gradle b/buildSrc/src/main/groovy/jadx-kotlin.gradle deleted file mode 100644 index 27dd994d9..000000000 --- a/buildSrc/src/main/groovy/jadx-kotlin.gradle +++ /dev/null @@ -1,12 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask - -plugins { - id 'org.jetbrains.kotlin.jvm' -} - -tasks.withType(KotlinCompilationTask.class).configureEach { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} diff --git a/buildSrc/src/main/groovy/jadx-library.gradle b/buildSrc/src/main/groovy/jadx-library.gradle deleted file mode 100644 index 5553bcda4..000000000 --- a/buildSrc/src/main/groovy/jadx-library.gradle +++ /dev/null @@ -1,79 +0,0 @@ -plugins { - id 'java-library' - id 'maven-publish' - id 'signing' -} - - -group = 'io.github.skylot' -version = jadxVersion - -java { - withJavadocJar() - withSourcesJar() -} - -publishing { - publications { - mavenJava(MavenPublication) { - artifactId = project.name - from components.java - versionMapping { - usage('java-api') { - fromResolutionOf('runtimeClasspath') - } - usage('java-runtime') { - fromResolutionResult() - } - } - pom { - name = project.name - description = 'Dex to Java decompiler' - url = 'https://github.com/skylot/jadx' - licenses { - license { - name = 'The Apache License, Version 2.0' - url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - developers { - developer { - id = 'skylot' - name = 'Skylot' - email = 'skylot@gmail.com' - url = 'https://github.com/skylot' - } - } - scm { - connection = 'scm:git:git://github.com/skylot/jadx.git' - developerConnection = 'scm:git:ssh://github.com:skylot/jadx.git' - url = 'https://github.com/skylot/jadx' - } - } - } - } - repositories { - maven { - def releasesRepoUrl = uri('https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/') - def snapshotsRepoUrl = uri('https://s01.oss.sonatype.org/content/repositories/snapshots/') - url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl - credentials { - username = project.properties['ossrhUser'].toString() - password = project.properties['ossrhPassword'].toString() - } - } - } -} - -signing { - required { gradle.taskGraph.hasTask("publish") } - sign publishing.publications.mavenJava -} - -javadoc { - if (JavaVersion.current().isJava9Compatible()) { - options.addBooleanOption('html5', true) - } - // disable 'missing' warnings - options.addStringOption('Xdoclint:all,-missing', '-quiet') -} diff --git a/buildSrc/src/main/kotlin/jadx-java.gradle.kts b/buildSrc/src/main/kotlin/jadx-java.gradle.kts new file mode 100644 index 000000000..f066cf190 --- /dev/null +++ b/buildSrc/src/main/kotlin/jadx-java.gradle.kts @@ -0,0 +1,51 @@ +plugins { + java + checkstyle +} + +val jadxVersion: String by rootProject.extra + +group = "io.github.skylot" +version = jadxVersion + +dependencies { + implementation("org.slf4j:slf4j-api:2.0.7") + compileOnly("org.jetbrains:annotations:24.0.1") + + testImplementation("ch.qos.logback:logback-classic:1.4.8") + testImplementation("org.hamcrest:hamcrest-library:2.2") + testImplementation("org.mockito:mockito-core:5.4.0") + testImplementation("org.assertj:assertj-core:3.24.2") + + testImplementation("org.junit.jupiter:junit-jupiter:5.9.3") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + testCompileOnly("org.jetbrains:annotations:24.0.1") +} + +repositories { + mavenCentral() + // required for `aapt-proto` and `r8` + google() +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +tasks { + compileJava { + options.encoding = "UTF-8" + } + jar { + manifest { + attributes("jadx-version" to jadxVersion) + } + } + test { + useJUnitPlatform() + maxParallelForks = Runtime.getRuntime().availableProcessors() + testLogging.showExceptions = true + } +} diff --git a/buildSrc/src/main/kotlin/jadx-kotlin.gradle.kts b/buildSrc/src/main/kotlin/jadx-kotlin.gradle.kts new file mode 100644 index 000000000..f3fef6f09 --- /dev/null +++ b/buildSrc/src/main/kotlin/jadx-kotlin.gradle.kts @@ -0,0 +1,12 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("jadx-java") + id("org.jetbrains.kotlin.jvm") +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } +} diff --git a/buildSrc/src/main/kotlin/jadx-library.gradle.kts b/buildSrc/src/main/kotlin/jadx-library.gradle.kts new file mode 100644 index 000000000..4c809dbd5 --- /dev/null +++ b/buildSrc/src/main/kotlin/jadx-library.gradle.kts @@ -0,0 +1,81 @@ +plugins { + id("jadx-java") + id("java-library") + id("maven-publish") + id("signing") +} + +val jadxVersion: String by rootProject.extra + +group = "io.github.skylot" +version = jadxVersion + +java { + withJavadocJar() + withSourcesJar() +} + +publishing { + publications { + create("mavenJava") { + artifactId = project.name + from(components["java"]) + versionMapping { + usage("java-api") { + fromResolutionOf("runtimeClasspath") + } + usage("java-runtime") { + fromResolutionResult() + } + } + pom { + name.set(project.name) + description.set("Dex to Java decompiler") + url.set("https://github.com/skylot/jadx") + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + developers { + developer { + id.set("skylot") + name.set("Skylot") + email.set("skylot@gmail.com") + url.set("https://github.com/skylot") + } + } + scm { + connection .set("scm:git:git://github.com/skylot/jadx.git") + developerConnection.set("scm:git:ssh://github.com:skylot/jadx.git") + url .set("https://github.com/skylot/jadx") + } + } + } + } + repositories { + maven { + val releasesRepoUrl = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") + val snapshotsRepoUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl + credentials { + username = project.properties["ossrhUser"].toString() + password = project.properties["ossrhPassword"].toString() + } + } + } +} + +signing { + isRequired = gradle.taskGraph.hasTask("publish") + sign(publishing.publications["mavenJava"]) +} + + +tasks.javadoc { + val stdOptions = options as StandardJavadocDocletOptions + stdOptions.addBooleanOption("html5", true) + // disable 'missing' warnings + stdOptions.addStringOption("Xdoclint:all,-missing", "-quiet") +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79e29d3e0ab67b14947c167a862655af9b..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 16170 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 delta 14892 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2c3425d49..a7c2bd1bd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,8 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionSha256Sum=38f66cd6eef217b4c35855bb11ea4e9fbc53594ccccb5fb82dfd317ef8c2c5a3 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cbb4..fcb6fca14 100755 --- a/gradlew +++ b/gradlew @@ -130,10 +130,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. diff --git a/jadx-cli/build.gradle b/jadx-cli/build.gradle deleted file mode 100644 index 75a29d291..000000000 --- a/jadx-cli/build.gradle +++ /dev/null @@ -1,44 +0,0 @@ -plugins { - id 'application' - - // use shadow only for application scripts, jar will be copied from jadx-gui - id 'com.github.johnrengelman.shadow' version '8.1.1' -} - -dependencies { - implementation(project(':jadx-core')) - implementation(project(':jadx-plugins-tools')) - - runtimeOnly(project(':jadx-plugins:jadx-dex-input')) - runtimeOnly(project(':jadx-plugins:jadx-java-input')) - runtimeOnly(project(':jadx-plugins:jadx-java-convert')) - runtimeOnly(project(':jadx-plugins:jadx-smali-input')) - runtimeOnly(project(':jadx-plugins:jadx-rename-mappings')) - runtimeOnly(project(':jadx-plugins:jadx-kotlin-metadata')) - runtimeOnly(project(':jadx-plugins:jadx-script:jadx-script-plugin')) - - implementation 'com.beust:jcommander:1.82' - implementation 'ch.qos.logback:logback-classic:1.4.8' -} - -application { - applicationName = 'jadx' - mainClass.set('jadx.cli.JadxCLI') - applicationDefaultJvmArgs = ['-Xms128M', '-XX:MaxRAMPercentage=70.0', '-XX:+UseG1GC'] -} -shadow { - mainClassName = application.mainClass.get() -} -shadowJar { - // shadow jar not needed - configurations = [] -} - -applicationDistribution.with { - into('') { - from '../.' - include 'README.md' - include 'NOTICE' - include 'LICENSE' - } -} diff --git a/jadx-cli/build.gradle.kts b/jadx-cli/build.gradle.kts new file mode 100644 index 000000000..ba96dea40 --- /dev/null +++ b/jadx-cli/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + id("jadx-java") + id("application") + + // use shadow only for application scripts, jar will be copied from jadx-gui + id("com.github.johnrengelman.shadow") version "8.1.1" +} + +dependencies { + implementation(project(":jadx-core")) + implementation(project(":jadx-plugins-tools")) + + runtimeOnly(project(":jadx-plugins:jadx-dex-input")) + runtimeOnly(project(":jadx-plugins:jadx-java-input")) + runtimeOnly(project(":jadx-plugins:jadx-java-convert")) + runtimeOnly(project(":jadx-plugins:jadx-smali-input")) + runtimeOnly(project(":jadx-plugins:jadx-rename-mappings")) + runtimeOnly(project(":jadx-plugins:jadx-kotlin-metadata")) + runtimeOnly(project(":jadx-plugins:jadx-script:jadx-script-plugin")) + + implementation("com.beust:jcommander:1.82") + implementation("ch.qos.logback:logback-classic:1.4.8") +} + +application { + applicationName = "jadx" + mainClass.set("jadx.cli.JadxCLI") + applicationDefaultJvmArgs = listOf("-Xms256M", "-XX:MaxRAMPercentage=70.0") + applicationDistribution.from("$rootDir") { + include("README.md") + include("NOTICE") + include("LICENSE") + } +} + +tasks.shadowJar { + // shadow jar not needed + configurations = listOf() +} diff --git a/jadx-core/build.gradle b/jadx-core/build.gradle deleted file mode 100644 index c6a3d4a86..000000000 --- a/jadx-core/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id 'jadx-library' -} - -dependencies { - api(project(":jadx-plugins:jadx-input-api")) - - implementation 'com.google.code.gson:gson:2.10.1' - - // TODO: move resources decoding to separate plugin module - implementation 'com.android.tools.build:aapt2-proto:8.0.2-9289358' - implementation 'com.google.protobuf:protobuf-java:3.23.3' // forcing latest version - - testImplementation 'org.apache.commons:commons-lang3:3.12.0' - - testImplementation(project(':jadx-plugins:jadx-dex-input')) - testRuntimeOnly(project(':jadx-plugins:jadx-smali-input')) - testRuntimeOnly(project(':jadx-plugins:jadx-java-convert')) - testRuntimeOnly(project(':jadx-plugins:jadx-java-input')) - testRuntimeOnly(project(':jadx-plugins:jadx-raung-input')) - - testImplementation 'org.eclipse.jdt:ecj:3.33.0' // from 3.34 compiled with Java 17 - testImplementation 'tools.profiler:async-profiler:2.9' -} - -test { - exclude '**/tmp/*' -} diff --git a/jadx-core/build.gradle.kts b/jadx-core/build.gradle.kts new file mode 100644 index 000000000..5ff70e06c --- /dev/null +++ b/jadx-core/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + id("jadx-library") +} + +dependencies { + api(project(":jadx-plugins:jadx-input-api")) + + implementation("com.google.code.gson:gson:2.10.1") + + // TODO: move resources decoding to separate plugin module + implementation("com.android.tools.build:aapt2-proto:8.0.2-9289358") + implementation("com.google.protobuf:protobuf-java:3.23.4") // forcing latest version + + testImplementation("org.apache.commons:commons-lang3:3.12.0") + + testImplementation(project(":jadx-plugins:jadx-dex-input")) + testRuntimeOnly(project(":jadx-plugins:jadx-smali-input")) + testRuntimeOnly(project(":jadx-plugins:jadx-java-convert")) + testRuntimeOnly(project(":jadx-plugins:jadx-java-input")) + testRuntimeOnly(project(":jadx-plugins:jadx-raung-input")) + + testImplementation("org.eclipse.jdt:ecj") { + version { + prefer("3.33.0") + strictly("[3.33, 3.34[") // from 3.34 compiled with Java 17 + } + } + testImplementation("tools.profiler:async-profiler:2.9") +} + +tasks.test { + exclude("**/tmp/*") +} diff --git a/jadx-gui/build.gradle b/jadx-gui/build.gradle deleted file mode 100644 index c3a598697..000000000 --- a/jadx-gui/build.gradle +++ /dev/null @@ -1,166 +0,0 @@ -plugins { - id 'jadx-kotlin' - id 'application' - id 'edu.sc.seis.launch4j' version '3.0.3' - id 'com.github.johnrengelman.shadow' version '8.1.1' - id 'org.beryx.runtime' version '1.13.0' -} - -dependencies { - implementation(project(':jadx-core')) - implementation(project(':jadx-cli')) - implementation(project(':jadx-plugins-tools')) - - // import mappings - implementation project(':jadx-plugins:jadx-rename-mappings') - - // jadx-script autocomplete support - implementation(project(':jadx-plugins:jadx-script:jadx-script-ide')) - implementation(project(':jadx-plugins:jadx-script:jadx-script-runtime')) - implementation 'org.jetbrains.kotlin:kotlin-scripting-common:1.8.22' - implementation 'com.fifesoft:autocomplete:3.3.1' - - // use KtLint for format and check jadx scripts - implementation 'com.pinterest.ktlint:ktlint-core:0.49.1' - implementation 'com.pinterest.ktlint:ktlint-rule-engine:0.49.1' - implementation 'com.pinterest.ktlint:ktlint-ruleset-standard:0.49.1' - - implementation 'com.beust:jcommander:1.82' - implementation 'ch.qos.logback:logback-classic:1.4.8' - implementation 'dev.dirs:directories:26' - - implementation 'com.fifesoft:rsyntaxtextarea:3.3.3' - implementation files('libs/jfontchooser-1.0.5.jar') - implementation 'hu.kazocsaba:image-viewer:1.2.3' - - implementation 'com.formdev:flatlaf:3.1.1' - implementation 'com.formdev:flatlaf-intellij-themes:3.1.1' - implementation 'com.formdev:flatlaf-extras:3.1.1' - implementation 'com.formdev:svgSalamander:1.1.4' - - implementation 'com.google.code.gson:gson:2.10.1' - implementation 'org.apache.commons:commons-lang3:3.12.0' - implementation 'org.apache.commons:commons-text:1.10.0' - - implementation 'io.reactivex.rxjava2:rxjava:2.2.21' - implementation "com.github.akarnokd:rxjava2-swing:0.3.7" - implementation 'com.android.tools.build:apksig:8.0.2' - implementation 'io.github.skylot:jdwp:2.0.0' - - testImplementation project(":jadx-core").sourceSets.test.output -} - -test { - exclude '**/tmp/*' -} - -application { - applicationName = 'jadx-gui' - mainClass.set('jadx.gui.JadxGUI') - applicationDefaultJvmArgs = [ - '-Xms128M', '-XX:MaxRAMPercentage=70.0', - '-XX:+UseG1GC', // only relevant for Java 8, starting with Java 9 G1GC is already the default GC - '-Dawt.useSystemAAFontSettings=lcd', '-Dswing.aatext=true', - '-Djava.util.Arrays.useLegacyMergeSort=true', - '-XX:+IgnoreUnrecognizedVMOptions', '--add-opens=java.base/java.lang=ALL-UNNAMED', // for ktlint formatter - ] -} - -applicationDistribution.with { - into('') { - from '../' - include 'README.md' - include 'NOTICE' - include 'LICENSE' - } -} - -jar { - manifest { - attributes( - "Main-Class": application.mainClass.get(), - "Class-Path": configurations.runtimeClasspath.collect { it.getName() }.join(' ') - ) - } -} - -shadow { - mainClassName = application.mainClass.get() -} -shadowJar { - mergeServiceFiles() -} - -startScripts { - doLast { - def str = windowsScript.text - str = str.replaceAll('java.exe', 'javaw.exe') - str = str.replaceAll('"%JAVA_EXE%" %DEFAULT_JVM_OPTS%', 'start "jadx-gui" /B "%JAVA_EXE%" %DEFAULT_JVM_OPTS%') - windowsScript.text = str - } -} - -launch4j { - mainClassName = application.mainClass.get() - copyConfigurable = [] - jarTask = project.tasks.shadowJar - icon = "${projectDir}/src/main/resources/logos/jadx-logo.ico" - outfile = "jadx-gui-${project.jadxVersion}.exe" - copyright = 'Skylot' - windowTitle = 'jadx' - companyName = 'jadx' - jreMinVersion = '11' - jvmOptions = application.getApplicationDefaultJvmArgs() - requires64Bit = true - initialHeapPercent = 5 - maxHeapSize = 4096 - maxHeapPercent = 70 - downloadUrl = 'https://www.oracle.com/java/technologies/downloads/#jdk17-windows' - bundledJrePath = project.hasProperty("bundleJRE") ? '%EXEDIR%/jre' : '%JAVA_HOME%' -} -createExe.dependsOn(jar) - -runtime { - addOptions('--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages') - addModules( - 'java.desktop', - 'java.naming', - 'java.xml', - ) - jpackage { - imageOptions = ['--icon', "${projectDir}/src/main/resources/logos/jadx-logo.ico"] - skipInstaller = true - targetPlatformName = "win" - } - launcher { - noConsole = true - } -} - -task copyDistWinWithJre(type: Copy, dependsOn: ['runtime', 'createExe']) { - group 'jadx' - destinationDir = new File(buildDir, "jadx-gui-${jadxVersion}-with-jre-win") - from(runtime.jreDir) { - include '**/*' - into 'jre' - } - from(createExe.outputs) { - include '*.exe' - } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} - -task distWinWithJre(type: Zip, dependsOn: ['copyDistWinWithJre']) { - group 'jadx' - destinationDirectory = buildDir - archiveFileName = "jadx-gui-${jadxVersion}-with-jre-win.zip" - from(copyDistWinWithJre.outputs) { - include '**/*' - } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} - -task addNewNLSLines(type: JavaExec) { - classpath = sourceSets.main.runtimeClasspath - mainClass.set('jadx.gui.utils.tools.NLSAddNewLines') -} diff --git a/jadx-gui/build.gradle.kts b/jadx-gui/build.gradle.kts new file mode 100644 index 000000000..25308b9f4 --- /dev/null +++ b/jadx-gui/build.gradle.kts @@ -0,0 +1,164 @@ +plugins { + id("jadx-kotlin") + id("application") + id("edu.sc.seis.launch4j") version "3.0.3" + id("com.github.johnrengelman.shadow") version "8.1.1" + id("org.beryx.runtime") version "1.13.0" +} + +dependencies { + implementation(project(":jadx-core")) + implementation(project(":jadx-cli")) + implementation(project(":jadx-plugins-tools")) + + // import mappings + implementation(project(":jadx-plugins:jadx-rename-mappings")) + + // jadx-script autocomplete support + implementation(project(":jadx-plugins:jadx-script:jadx-script-ide")) + implementation(project(":jadx-plugins:jadx-script:jadx-script-runtime")) + implementation("org.jetbrains.kotlin:kotlin-scripting-common:1.9.0") + implementation("com.fifesoft:autocomplete:3.3.1") + + // use KtLint for format and check jadx scripts + implementation("com.pinterest.ktlint:ktlint-rule-engine:0.50.0") + implementation("com.pinterest.ktlint:ktlint-ruleset-standard:0.50.0") + + implementation("com.beust:jcommander:1.82") + implementation("ch.qos.logback:logback-classic:1.4.8") + implementation("dev.dirs:directories:26") + + implementation("com.fifesoft:rsyntaxtextarea:3.3.3") + implementation(files("libs/jfontchooser-1.0.5.jar")) + implementation("hu.kazocsaba:image-viewer:1.2.3") + + implementation("com.formdev:flatlaf:3.1.1") + implementation("com.formdev:flatlaf-intellij-themes:3.1.1") + implementation("com.formdev:flatlaf-extras:3.1.1") + implementation("com.formdev:svgSalamander:1.1.4") + + implementation("com.google.code.gson:gson:2.10.1") + implementation("org.apache.commons:commons-lang3:3.12.0") + implementation("org.apache.commons:commons-text:1.10.0") + + implementation("io.reactivex.rxjava2:rxjava:2.2.21") + implementation("com.github.akarnokd:rxjava2-swing:0.3.7") + implementation("com.android.tools.build:apksig:8.0.2") + implementation("io.github.skylot:jdwp:2.0.0") + + testImplementation(project(":jadx-core").dependencyProject.sourceSets.getByName("test").output) +} + +val jadxVersion: String by rootProject.extra + +tasks.test { + exclude("**/tmp/*") +} + +application { + applicationName = ("jadx-gui") + mainClass.set("jadx.gui.JadxGUI") + applicationDefaultJvmArgs = listOf( + "-Xms128M", + "-XX:MaxRAMPercentage=70.0", + "-Dawt.useSystemAAFontSettings=lcd", + "-Dswing.aatext=true", + "-Djava.util.Arrays.useLegacyMergeSort=true", + "-XX:+IgnoreUnrecognizedVMOptions", + "--add-opens=java.base/java.lang=ALL-UNNAMED", // for ktlint formatter + ) + applicationDistribution.from("$rootDir") { + include("README.md") + include("NOTICE") + include("LICENSE") + } +} + +tasks.jar { + manifest { + attributes(mapOf("Main-Class" to application.mainClass.get())) + } +} + +tasks.shadowJar { + mergeServiceFiles() + manifest { + from(project.tasks.jar.get().manifest) + } +} + +tasks.existing(CreateStartScripts::class) { + doLast { + val newContent = windowsScript.readText() + .replace("java.exe", "javaw.exe") + .replace("\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS%", "start \"jadx-gui\" /B \"%JAVA_EXE%\" %DEFAULT_JVM_OPTS%") + windowsScript.writeText(newContent) + } +} + +launch4j { + mainClassName = application.mainClass.get() + copyConfigurable.set(listOf()) + setJarTask(tasks.shadowJar.get()) + icon = "$projectDir/src/main/resources/logos/jadx-logo.ico" + outfile = "jadx-gui-$jadxVersion.exe" + copyright = "Skylot" + windowTitle = "jadx" + companyName = "jadx" + jreMinVersion = "11" + jvmOptions = application.applicationDefaultJvmArgs.toSet() + requires64Bit = true + initialHeapPercent = 5 + maxHeapSize = 4096 + maxHeapPercent = 70 + downloadUrl = "https://www.oracle.com/java/technologies/downloads/#jdk17-windows" + bundledJrePath = if (project.hasProperty("bundleJRE")) "%EXEDIR%/jre" else "%JAVA_HOME%" +} + +runtime { + addOptions("--strip-debug", "--compress", "2", "--no-header-files", "--no-man-pages") + addModules( + "java.desktop", + "java.naming", + "java.xml", + ) + jpackage { + imageOptions = listOf("--icon", "$projectDir/src/main/resources/logos/jadx-logo.ico") + skipInstaller = true + targetPlatformName = "win" + } + launcher { + noConsole = true + } +} + +val copyDistWinWithJre by tasks.registering(Copy::class) { + group = "jadx" + dependsOn(tasks.named("runtime"), tasks.named("createExe")) + from(runtime.jreDir) { + include("**/*") + into("jre") + } + from(tasks.named("createExe").get().outputs) { + include("*.exe") + } + into(File(buildDir, "jadx-gui-$jadxVersion-with-jre-win")) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +val distWinWithJre by tasks.registering(Zip::class) { + group = "jadx" + dependsOn(copyDistWinWithJre) + archiveFileName.set("jadx-gui-$jadxVersion-with-jre-win.zip") + from(copyDistWinWithJre.get().outputs) { + include("**/*") + } + into(buildDir) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + +val addNewNLSLines by tasks.registering(JavaExec::class) { + group = "jadx" + classpath = sourceSets.main.get().runtimeClasspath + mainClass.set("jadx.gui.utils.tools.NLSAddNewLines") +} diff --git a/jadx-plugins/jadx-dex-input/build.gradle b/jadx-plugins/jadx-dex-input/build.gradle deleted file mode 100644 index 35ac8b7d8..000000000 --- a/jadx-plugins/jadx-dex-input/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - id 'jadx-library' -} - -dependencies { - api(project(":jadx-core")) - - // TODO: finish own smali printer - implementation 'org.smali:baksmali:2.5.2' - // force latest version for smali - constraints { - implementation 'com.google.guava:guava:30.1.1-jre' - implementation 'com.beust:jcommander:1.81' - } - - // compile smali files in tests - testImplementation('org.smali:smali:2.5.2') { - exclude(group: 'junit', module: 'junit') // ignore junit 4 transitive dependency - } -} diff --git a/jadx-plugins/jadx-dex-input/build.gradle.kts b/jadx-plugins/jadx-dex-input/build.gradle.kts new file mode 100644 index 000000000..8a2286ead --- /dev/null +++ b/jadx-plugins/jadx-dex-input/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("jadx-library") +} + +dependencies { + api(project(":jadx-core")) + + // TODO: finish own smali printer + implementation("org.smali:baksmali:2.5.2") + // force latest version for smali + constraints { + implementation("com.google.guava:guava:30.1.1-jre") + implementation("com.beust:jcommander:1.81") + } + + // compile smali files in tests + testImplementation("org.smali:smali:2.5.2") { + exclude(group = "junit", module = "junit") // ignore junit 4 transitive dependency + } +} diff --git a/jadx-plugins/jadx-input-api/build.gradle b/jadx-plugins/jadx-input-api/build.gradle deleted file mode 100644 index c4c8599f4..000000000 --- a/jadx-plugins/jadx-input-api/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id 'jadx-library' -} diff --git a/jadx-plugins/jadx-input-api/build.gradle.kts b/jadx-plugins/jadx-input-api/build.gradle.kts new file mode 100644 index 000000000..d974fd945 --- /dev/null +++ b/jadx-plugins/jadx-input-api/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("jadx-library") +} diff --git a/jadx-plugins/jadx-java-convert/build.gradle b/jadx-plugins/jadx-java-convert/build.gradle deleted file mode 100644 index 0a438e496..000000000 --- a/jadx-plugins/jadx-java-convert/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id 'jadx-library' -} - -dependencies { - api(project(":jadx-core")) - - implementation(project(":jadx-plugins:jadx-dex-input")) - implementation('com.jakewharton.android.repackaged:dalvik-dx:11.0.0_r3') - implementation('com.android.tools:r8:8.0.40') - - implementation 'org.ow2.asm:asm:9.5' -} diff --git a/jadx-plugins/jadx-java-convert/build.gradle.kts b/jadx-plugins/jadx-java-convert/build.gradle.kts new file mode 100644 index 000000000..ef2339c3f --- /dev/null +++ b/jadx-plugins/jadx-java-convert/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("jadx-library") +} + +dependencies { + api(project(":jadx-core")) + + implementation(project(":jadx-plugins:jadx-dex-input")) + implementation("com.jakewharton.android.repackaged:dalvik-dx:11.0.0_r3") + implementation("com.android.tools:r8:8.0.40") + + implementation("org.ow2.asm:asm:9.5") +} diff --git a/jadx-plugins/jadx-java-input/build.gradle b/jadx-plugins/jadx-java-input/build.gradle.kts similarity index 63% rename from jadx-plugins/jadx-java-input/build.gradle rename to jadx-plugins/jadx-java-input/build.gradle.kts index 8fa38a91c..b6ca9f927 100644 --- a/jadx-plugins/jadx-java-input/build.gradle +++ b/jadx-plugins/jadx-java-input/build.gradle.kts @@ -1,12 +1,12 @@ plugins { - id 'jadx-library' + id("jadx-library") } dependencies { api(project(":jadx-core")) // show bytecode disassemble - implementation 'io.github.skylot:raung-disasm:0.1.0' + implementation("io.github.skylot:raung-disasm:0.1.0") testImplementation(project(":jadx-core")) } diff --git a/jadx-plugins/jadx-raung-input/build.gradle b/jadx-plugins/jadx-raung-input/build.gradle.kts similarity index 62% rename from jadx-plugins/jadx-raung-input/build.gradle rename to jadx-plugins/jadx-raung-input/build.gradle.kts index b9b91a32e..80cb98ae0 100644 --- a/jadx-plugins/jadx-raung-input/build.gradle +++ b/jadx-plugins/jadx-raung-input/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id 'jadx-library' + id("jadx-library") } dependencies { @@ -7,5 +7,5 @@ dependencies { implementation(project(":jadx-plugins:jadx-java-input")) - implementation('io.github.skylot:raung-asm:0.1.0') + implementation("io.github.skylot:raung-asm:0.1.0") } diff --git a/jadx-plugins/jadx-rename-mappings/build.gradle b/jadx-plugins/jadx-rename-mappings/build.gradle.kts similarity index 52% rename from jadx-plugins/jadx-rename-mappings/build.gradle rename to jadx-plugins/jadx-rename-mappings/build.gradle.kts index 2feb3dc9f..cf0b71c5e 100644 --- a/jadx-plugins/jadx-rename-mappings/build.gradle +++ b/jadx-plugins/jadx-rename-mappings/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - id 'jadx-library' + id("jadx-library") - id 'com.github.johnrengelman.shadow' version '8.1.1' + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { @@ -10,17 +10,18 @@ dependencies { // TODO: Switch back to upstream once this PR gets merged: // https://github.com/FabricMC/mapping-io/pull/19 // implementation 'net.fabricmc:mapping-io:0.3.0' - api(files('libs/mapping-io-0.4.0-SNAPSHOT.jar')) + api(files("libs/mapping-io-0.4.0-SNAPSHOT.jar")) constraints { - runtimeOnly 'org.ow2.asm:asm:9.5' + runtimeOnly("org.ow2.asm:asm:9.5") } } publishing { publications { - shadow(MavenPublication) { publication -> - project.shadow.component(publication) + create("maven") { + from(components["java"]) + artifact(tasks["shadowJar"]) } } } diff --git a/jadx-plugins/jadx-script/examples/build.gradle.kts b/jadx-plugins/jadx-script/examples/build.gradle.kts index a379e42b4..e565baadc 100644 --- a/jadx-plugins/jadx-script/examples/build.gradle.kts +++ b/jadx-plugins/jadx-script/examples/build.gradle.kts @@ -23,7 +23,7 @@ sourceSets { kotlin.srcDirs( "scripts", "scripts/gui", - "context" + "context", ) } } diff --git a/jadx-plugins/jadx-script/jadx-script-plugin/build.gradle.kts b/jadx-plugins/jadx-script/jadx-script-plugin/build.gradle.kts index b3d0c5241..27aaa7107 100644 --- a/jadx-plugins/jadx-script/jadx-script-plugin/build.gradle.kts +++ b/jadx-plugins/jadx-script/jadx-script-plugin/build.gradle.kts @@ -6,9 +6,9 @@ plugins { dependencies { implementation(project(":jadx-plugins:jadx-script:jadx-script-runtime")) - implementation(kotlin("scripting-common")) - implementation(kotlin("scripting-jvm")) - implementation(kotlin("scripting-jvm-host")) + implementation(kotlin("scripting-common")) + implementation(kotlin("scripting-jvm")) + implementation(kotlin("scripting-jvm-host")) implementation("io.github.microutils:kotlin-logging-jvm:3.0.5") diff --git a/jadx-plugins/jadx-script/jadx-script-runtime/build.gradle.kts b/jadx-plugins/jadx-script/jadx-script-runtime/build.gradle.kts index 5951f5fc3..5994a88c8 100644 --- a/jadx-plugins/jadx-script/jadx-script-runtime/build.gradle.kts +++ b/jadx-plugins/jadx-script/jadx-script-runtime/build.gradle.kts @@ -22,5 +22,4 @@ dependencies { runtimeOnly(project(":jadx-plugins:jadx-java-convert")) runtimeOnly(project(":jadx-plugins:jadx-java-input")) runtimeOnly(project(":jadx-plugins:jadx-raung-input")) - } diff --git a/jadx-plugins/jadx-smali-input/build.gradle b/jadx-plugins/jadx-smali-input/build.gradle deleted file mode 100644 index 25955b2fa..000000000 --- a/jadx-plugins/jadx-smali-input/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -plugins { - id 'jadx-library' -} - -dependencies { - api(project(":jadx-core")) - - implementation(project(":jadx-plugins:jadx-dex-input")) - - implementation('org.smali:smali:2.5.2') { - exclude(group: 'junit', module: 'junit') // ignore junit 4 transitive dependency - } - // force latest version for smali - constraints { - implementation 'com.google.guava:guava:30.1.1-jre' - implementation 'com.beust:jcommander:1.81' - } -} diff --git a/jadx-plugins/jadx-smali-input/build.gradle.kts b/jadx-plugins/jadx-smali-input/build.gradle.kts new file mode 100644 index 000000000..d7419a66f --- /dev/null +++ b/jadx-plugins/jadx-smali-input/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("jadx-library") +} + +dependencies { + api(project(":jadx-core")) + + implementation(project(":jadx-plugins:jadx-dex-input")) + + implementation("org.smali:smali:2.5.2") { + exclude(group = "junit", module = "junit") // ignore junit 4 transitive dependency + } + // force latest version for smali + constraints { + implementation("com.google.guava:guava:30.1.1-jre") + implementation("com.beust:jcommander:1.81") + } +}