I have a kotlin multi-platform project for which I am creating a release framework. My release framework is bigger than the debug framework.
Following is the gradle task to create the framework:
task packForXcode(type: Sync) {
final File frameworkDir = new File(buildDir, "xcode-frameworks")
final String mode = 'RELEASE'
final def framework = kotlin.targets.iosArm64.binaries.getFramework(frameworkName, mode)
inputs.property "mode", mode
dependsOn framework.linkTask
from { framework.outputFile.parentFile }
into frameworkDir
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$#\n"
setExecutable(true)
}
}
}
The framework file generated when mode = 'RELEASE' is 5.3 kb but when mode = 'DEBUG' one is 3.8 Kb.
I have 2 questions:
Is this expected ?
How can reduce or remove unused code while creating such a framework like R8/pro-guard in android ?
This result can be expected, as only release frameworks are embedding bitcode, debug ones have only bitcode markers(see some details here). Unused code should be already removed by the compiler, so there is no need to apply other tools here.
Related
I have a gradle kotlin project, and I'm generating a kotlin file from a Rust project, so it ends up in a totally different place with no gradle project structure, etc.
How do I import this file into my gradle project?
It has its own package but it's a completely standalone file. This is my gradle file:
rootProject.name = "my_project"
include("app")
It's a desktop project, NOT android.
My build.gradle.kts:
plugins {
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
id("org.jetbrains.kotlin.jvm") version "1.5.31"
// Apply the application plugin to add support for building a CLI application in Java.
application
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// This dependency is used by the application.
implementation("com.google.guava:guava:30.1.1-jre")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
// Define the main class for the application.
mainClass.set("my_project.ffi.AppKt")
}
Adding the following code to your build.gradle.kts should do the trick (tested with Gradle 7.3.2):
// TODO: replace this dummy task with the task from your Rust project which
// generates the Kotlin source directory. Make sure that the generated
// directory (with the Kotlin file(s)) is the task output directory.
val rustTask by tasks.registering(Copy::class) {
// To test this, I had simply put a Kotlin file into this "somewhere"
// directory.
from("somewhere")
into(temporaryDir)
}
sourceSets {
main {
java {
srcDir(rustTask)
}
}
}
tasks {
compileKotlin {
dependsOn(rustTask)
}
}
So, we’re simply adding the generated sources as an additional source directory to the default SourceSet which is consumed by the compileKotlin task. In addition, we make sure that the sources are generated before compileKotlin runs.
I'm trying to get the new (alpha) android datastore using protobuf support configured in gradle using Kotlin DSL (build.gradle.kts). The first attempts are not generating any java source classes from the xxx.proto (made-up name) file that is present. The protobuf plugin is generating the correct android tasks, but running them generates nothing, so obviously the default setup is not finding the directory my initial xxx.proto file is located in. The existing doc is thin on gradle setup, especially for Kotlin Gradle DSL (most all the gradle doc from google so far is for groovy), and my initial attempts at defining the location of the xxx.proto file are not working.
Does anyone have or has anyone seen working gradle config that specifies a custom source directory for .proto file(s) using Kotlin (build.gradle.kts)?
Got it working after some experimentation and floundering, but a hack is involved. If anyone can suggest improvements, it would be appreciated. In case this is useful here are the config snippets from the working setup. Module is kotlin 1.4.21-2 multiplatform with android, ios64, and jvm targets, with more planned. It has the KMP default setup for source directories:
The .proto file is in src/androidMain/proto subdirectory.
build.gradle.kts snippets are below. All the changes are in the android block, except for the plugin of course:
plugins {
id("com.android.library")
kotlin("multiplatform")
id("kotlinx-atomicfu")
kotlin("plugin.serialization") version Versions.kotlinVersion
id("com.google.protobuf") version "0.8.14"
}
...
kotlin {
... no changes here
}
...
android {
...
sourceSets {
...
getByName("main") {
manifest.srcFile("src/androidMain/AndroidManifest.xml")
java.srcDirs("src/androidMain/kotlin")
assets.srcDirs(File("src/commonMain/resources"))
withGroovyBuilder {
"proto" {
"srcDir" ("src/androidMain/proto")
}
}
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:4.0.0-rc-2"
}
plugins {
id("javalite") { artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0" }
}
generateProtoTasks {
all().forEach { task ->
task.builtins {
id("java") {
option("lite")
}
}
task.plugins{
}
}
}
}
dependencies {
api("com.google.protobuf:protobuf-javalite:4.0.0-rc-2")
implementation("androidx.datastore:datastore:1.0.0-alpha05")
...
}
}
Note the withGroovyBuilder hack in the android sourceset - the srcdir definition is required for the plugin to find the .proto file I had, but in the current version of the plugin I couldn't figure out the correct Kotlin DSL syntax. Seems like the plugin needs to define a Kotlin extension function to make this work better.
It would be really nice if instead of requiring this stuff, that the datastore stuff could use the protobuf serialization available with kotlinx.serialization, and skip this java code generation step in gradle all together. But I'm sure that's down the road...
Anyway, thanks in advance if anyone has improvements etc...
I want to build a CLI tool with Kotlin Multiplatform which runs on Linux, Macos and Windows.
But I am struggling with setting up my build.gradle and my project structure. I am using IntelliJ IDEA 2020.1 and created my basic project with File -> New -> Project -> Kotlin / Native | Gradle
Currently I am looking through guides from kotlinlang.org but I am more falling then achieving something.
So far my build.gradle looks as follows:
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.72'
}
repositories {
mavenCentral()
}
kotlin {
// For ARM, should be changed to iosArm32 or iosArm64
// For Linux, should be changed to e.g. linuxX64
// For MacOS, should be changed to e.g. macosX64
// For Windows, should be changed to e.g. mingwX64
linuxX64("linux") {
}
mingwX64("mingw") {
}
macosX64("macos") {
binaries {
executable {
// Change to specify fully qualified name of your application's entry point:
entryPoint = 'sample.main'
// Specify command-line arguments, if necessary:
runTask?.args('')
}
}
}
sourceSets {
commonMain {
kotlin.srcDir('src/main')
resources.srcDir('src/res')
dependencies {
implementation kotlin('stdlib-common')
implementation "com.github.ajalt:clikt-multiplatform:2.7.0"
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
macosX64().compilations.test.defaultSourceSet {
dependsOn commonMain
}
// Note: To enable common source sets please comment out
'kotlin.import.noCommonSourceSets' property
// in gradle.properties file and re-import your project in IDE.
macosMain {
}
macosTest {
}
}
}
wrapper {
gradleVersion = "6.4.1"
distributionType = "ALL"
}
And my project structure is still basic:
Project structure
Formerly I only worked on Android Projects with Kotlin, and I guess I am spoiled with gradle as Android generates the most basic stuff and everything is working without doing that much.
I understand that I need to create packages like linuxMain and mingwMain, but where to I put common sourcesets? I tried to create a package called commonMain, but it won't even let me create Kotlin files in that package.
When I am finished I want to have (in the best case) one common source set and one entry point for all my targets. Is this even possible?
As far as I can see, you specify your commonMain source set's source locations as /src/main/. By default, it's usually set onto /src/commonMain/kotlin/. So if you will remove those srcDir settings and create a .kt file in your /src/commonMain/kotlin/ folder, everything should work fine. Also, I hope you have removed 'kotlin.import.noCommonSourceSets' property from your gradle.properties as your script recommended.
I started develop kotlin multiplatform and I developed a simple lib for test. I can exported .jar file for android but I can't export .framework file for iOS.
I reviewed other project but I didn't find my issue.
my Gradle script for lib is:
apply plugin: 'kotlin-multiplatform'
kotlin {
targets {
final def iOSTarget =
System.getenv('SDK_NAME')?.startsWith("iphoneos") \
? presets.iosArm64 : presets.iosX64
fromPreset(iOSTarget, 'iOS') {
compilations.main.outputKinds('FRAMEWORK')
}
fromPreset(presets.jvm, 'android')
}
sourceSets {
core.dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib-common'
}
android.dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib'
}
}
Have you added the task to build the actual framework? If not, try adding this code at the end of your build.gradle file:
task packForXCode(type: Sync) {
final File frameworkDir = new File(buildDir, "xcode-frameworks")
final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'
inputs.property "mode", mode
dependsOn kotlin.targets.iOS.compilations.main.linkTaskName("FRAMEWORK", mode)
from { kotlin.targets.iOS.compilations.main.getBinary("FRAMEWORK", mode).parentFile }
into frameworkDir
doLast {
new File(frameworkDir, 'gradlew').with {
text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$#\n"
setExecutable(true)
}
}
}
tasks.build.dependsOn packForXCode
The iOS framework will be available on the build/xcode-frameworks directory of your library.
You'll have to configure also your Xcode project to use the framework. For further details you can read Setting up Framework Dependency in Xcode.
What do you mean by "exporting a framework"? Are you going to use it from another Gradle project or from XCode or from something else?
P.S. Sorry for asking in answers: just have no enough reputation to leave a comment. So I think it would be more convenient to discuss you problem in issues at GitHub.
I am looking to use Gradle to build my Groovy / Grails based project for which we are using IntelliJ Idea as the IDE.
I am using IntelliJ version 11.1.4, Gradle version 1.2.
My project is a configured as a multi project build with various Groovy & Grails subprojects.
I was hoping that this would give me the same kind of IDE support that I would get if managing the build through Maven, such as:
Automatic dependency management (import new dependencies to IntelliJ when added to various build.gradle)
Build DSL support
Execution of build tasks
Use of the underlying build system (gradle) by the IDE when performing builds\
I have imported my project into IntelliJ by opening the root build.gradle file.
So far I am coming up against a few annoying problems:
IntelliJ is not recognising (or recognising randomly) changes to dependencies in the build.gradle files and therefore dependencies are not being updated.
The gradle "idea" plugin doesn't seem to work with multi module projects.
How are people working with Gradle inside IntelliJ? Are you managing dependencies manually within IntelliJ??
I have been using Gradle "idea" plugin for some time now and it works very well. Since "idea" plugin simply generates IntelliJ project configuration files it is somewhat limited in what you can do with it, but nevertheless, I have had more success with it compared to the IntelliJ gradle support (JetGradle thing).
Gradle "idea" plugin does work with multi-module projects, never had a problem with that. I always put parent project configuration in master folder (see Initialization chapter), which seems to work. Never tried the nested structure though.
In order to do additional IntelliJ configuration you can do some .ipr and .iml tinkering from gradle, or alternatively try using one of my plugins (see Utilities plugin) which will do most of the tinkering for you.
In the end I went with rodion's suggestion above and used the idea plugin. It turns out I had not configured it properly first time I tried it (only applied the plugin to the master project, not subprojects).
I only found this out after writing my own task to update the dependencies for an IntelliJ project (based on the .idea directory layout project structure). I am going to use the plugin as there will be less maintenance, but here is my solution for posterity and in case it is useful to anyone:
ext {
intelliJLibraryDir = "$gradle.rootProject.rootDir/.idea/libraries"
userHomeDir = gradle.gradleUserHomeDir.parent
}
task cleanIntelliJLibraries << {
ant.delete (includeEmptyDirs: 'true') {
fileset(dir: intelliJLibraryDir, includes: '*.xml')
}
}
task createIntelliJLibraries(dependsOn: cleanIntelliJLibraries) << {
// The unique set of dependency artifacts across all subprojects
def uniqueProjectArtifacts = subprojects.collectMany {
if (it.configurations.compile) {
it.configurations.compile.resolvedConfiguration.resolvedArtifacts.findAll {
it.moduleVersion.id.group != "my.project"
}
}
else { [] }
}.unique()
// Output a library xml file for each of the dependency artifacts
uniqueProjectArtifacts.each { artifact ->
def artifactPath = artifact.file.path
def artifactName = artifact.moduleVersion.id.with { "$group:$name:$version" }
def intelliJLibraryPath = artifactPath.replace(userHomeDir, '$USER_HOME$')
def intelliJLibraryFileName = "Gradle__$artifactName".replace(':', '_').replace('.','_') + ".xml"
new File("$intelliJLibraryDir/$intelliJLibraryFileName").withWriter { writer ->
def dependencyXML = new MarkupBuilder( writer )
dependencyXML.component (name: "libraryTable") {
library (name: "Gradle: $artifactName") {
CLASSES {
root (url: "jar://$intelliJLibraryPath!/")
}
JAVADOC {}
SOURCES {}
}
}
}
}
}
task updateIntelliJModules(dependsOn: createIntelliJLibraries) << {
subprojects.each { project ->
def root = new XmlSlurper().parse(new File("${project.name}.iml"))
// Remove the existing dependencies
root.component.orderEntry.findAll { it.#type == "library" && it.#level == "project" }.replaceNode {}
// Add in the new dependencies
if (project.configurations.compile) {
project.configurations.compile.resolvedConfiguration.resolvedArtifacts.findAll {
it.moduleVersion.id.group != "my.project"
}.each { artifact ->
def artifactName = artifact.moduleVersion.id.with { "Gradle: $group:$name:$version" }
root.component.appendNode {
orderEntry (type: "library", exported: "", name: artifactName, level: "project")
}
}
}
def outputBuilder = new StreamingMarkupBuilder()
new File("${project.name}.iml").withWriter { writer ->
groovy.xml.XmlUtil.serialize(outputBuilder.bind{ mkp.yield root }, writer)
}
}
}