Skip to content

Commit

Permalink
Merge pull request #508 from RBusarow/release_0.15.0
Browse files Browse the repository at this point in the history
create release 0.15.0
  • Loading branch information
RBusarow authored Jul 27, 2022
2 parents 0350d44 + 4f171c5 commit 9a8d4d6
Show file tree
Hide file tree
Showing 16 changed files with 1,346 additions and 1 deletion.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
# 0.15.0

The `0.15.0` version is built using these major dependencies:

| library | version |
|:--------------------|:--------|
| Anvil | 2.4.1 |
| Dagger | 2.43 |
| Kotlin | 1.7.0 |
| androidx.compose | 1.2.0 |
| androidx.core | 1.2.0 |
| androidx.work | 2.7.1 |
| androidx.activity | 1.5.1 |
| androidx.fragment | 1.5.1 |
| androidx.lifecycle | 2.5.1 |
| androidx.savedstate | 1.2.0 |

### 🐛 Bug Fixes

- Allow Fragment::arg with nullable parameters [@oldwomanjosiah](https://github.com/oldwomanjosiah) ([#420](https://github.com/rbusarow/Tangle/pull/420))

### Contributors

@RBusarow, @oldwomanjosiah

# 0.14.1

### 🐛 Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/tangle/builds/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ package tangle.builds

const val GROUP = "com.rickbusarow.tangle"
const val PLUGIN_ID = "com.rickbusarow.tangle"
const val VERSION_NAME = "0.15.0-SNAPSHOT"
const val VERSION_NAME = "0.15.1-SNAPSHOT"
const val SOURCE_WEBSITE = "https://github.com/rbusarow/Tangle"
const val DOCS_WEBSITE = "https://rbusarow.github.io/Tangle"
25 changes: 25 additions & 0 deletions website/src/pages/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## 0.15.0

The `0.15.0` version is built using these major dependencies:

| library | version |
|:--------------------|:--------|
| Anvil | 2.4.1 |
| Dagger | 2.43 |
| Kotlin | 1.7.0 |
| androidx.compose | 1.2.0 |
| androidx.core | 1.2.0 |
| androidx.work | 2.7.1 |
| androidx.activity | 1.5.1 |
| androidx.fragment | 1.5.1 |
| androidx.lifecycle | 2.5.1 |
| androidx.savedstate | 1.2.0 |

#### 🐛 Bug Fixes

- Allow Fragment::arg with nullable parameters [@oldwomanjosiah](https://github.com/oldwomanjosiah) ([#420](https://github.com/rbusarow/Tangle/pull/420))

#### Contributors

@RBusarow, @oldwomanjosiah

## 0.14.1

#### 🐛 Bug Fixes
Expand Down
55 changes: 55 additions & 0 deletions website/versioned_docs/version-0.15.0/benchmarks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
id: benchmarks

sidebar_label: Benchmarks

title: Benchmarks
---

The Tangle project has the ability to generate test projects and run synthetic benchmarks against
it, using [Gradle-Profiler](https://github.com/gradle/gradle-profiler).

For the time being, the intent of these benchmarks is to provide a hermetic comparison between Hilt
and Tangle's build times, with as few variables as possible.

The generated test projects represent best-case scenarios, in that no library module depends upon
any other library module, and each library module only has a single empty `Fragment` and
empty `ViewModel`. The build speed percentage gain from using Tangle is most likely higher than
anything which could be observed in a real world application.

To run these tests yourself, [check out the Tangle project](https://github.com/RBusarow/tangle) and
run `./gradlew profile`. The generated code is in `$rootDir/build/benchmark-project`.

The generated benchmark project is also hosted on
GitHub [here](https://github.com/RBusarow/tangle-benchmark-project), with different branches for
different project sizes.

## The results

These tests were all run on a water-cooled 12-core 4.3GHz hackintosh with 32GB of ram. I chose that
machine because it has excellent cooling. A MacBook Pro will start overheating and thermal
throttling during prolonged benchmarking, skewing the results.

### 100 modules

Tangle's mean execution time was a 20.23% reduction from Hilt's mean.

[full results from Gradle Profile here](@site/static/benchmark/benchmark_100.html)

![Hilt vs Tangle results, 100 modules](/img/benchmark_100.png "Hilt vs Tangle results, 100 modules")

### 10 modules

Tangle's mean execution time was an 11.67% reduction from Hilt's mean. This is less significant
because the Tangle test project still needs to generate a Component using Kapt/Dagger, and that cost
is relatively static regardless of benchmark size. It's also comparable to the static cost of
component generation in a Hilt project. In a real world project with a much more complicated Dagger
graph, component generation should take longer.

It's also worth noting that an "11.67% reduction" in this case really just means that the build took
18 seconds instead of 20 seconds. For a project of this size, it's safe to say that the decision
should be made based upon API surface rather than build performance.

[full results from Gradle Profile here](@site/static/benchmark/benchmark_10.html)

![Hilt vs Tangle results, 10 modules](/img/benchmark_10.png "Hilt vs Tangle results, 100 modules")
129 changes: 129 additions & 0 deletions website/versioned_docs/version-0.15.0/configuration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
id: configuration
sidebar_label: Configuration
title: Configuration

---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

## Gradle

The simple way to apply Tangle is to just [apply the Gradle plugin](gradle-plugin).

You can also just add dependencies yourself, without applying the plugin:

<Tabs groupId="language"
defaultValue="Kotlin Dependencies"
values={[
{label: 'Kotlin Dependencies', value: 'Kotlin Dependencies'},
{label: 'Groovy Dependencies', value: 'Groovy Dependencies'},
]}>

<TabItem value="Kotlin Dependencies">

```kotlin
// any Android module's build.gradle.kts

plugins {
id("android-library") // or application, etc.
kotlin("android")
id("com.squareup.anvil")
}

dependencies {

// Fragments
api("com.rickbusarow.tangle:tangle-fragment-api:0.15.0-SNAPSHOT")
anvil("com.rickbusarow.tangle:tangle-fragment-compiler:0.15.0-SNAPSHOT")

// ViewModels
api("com.rickbusarow.tangle:tangle-viewmodel-api:0.15.0-SNAPSHOT")
anvil("com.rickbusarow.tangle:tangle-viewmodel-compiler:0.15.0-SNAPSHOT")

// optional Activity ViewModel support
implementation("com.rickbusarow.tangle:tangle-viewmodel-activity:0.15.0-SNAPSHOT")

// optional Compose ViewModel support
implementation("com.rickbusarow.tangle:tangle-viewmodel-compose:0.15.0-SNAPSHOT")

// optional Fragment ViewModel support
implementation("com.rickbusarow.tangle:tangle-viewmodel-fragment:0.15.0-SNAPSHOT")

// WorkManager
api("com.rickbusarow.tangle:tangle-work-api:0.15.0-SNAPSHOT")
anvil("com.rickbusarow.tangle:tangle-work-compiler:0.15.0-SNAPSHOT")
}
```

</TabItem>

<TabItem value="Groovy Dependencies">

```groovy
// any Android module's build.gradle
plugins {
id 'android-library' // or application, etc.
kotlin("android")
id 'com.squareup.anvil'
}
dependencies {
// Fragments
api 'com.rickbusarow.tangle:tangle-fragment-api:0.15.0-SNAPSHOT'
anvil 'com.rickbusarow.tangle:tangle-fragment-compiler:0.15.0-SNAPSHOT'
// ViewModels
api 'com.rickbusarow.tangle:tangle-viewmodel-api:0.15.0-SNAPSHOT'
anvil 'com.rickbusarow.tangle:tangle-viewmodel-compiler:0.15.0-SNAPSHOT'
// optional Activity ViewModel support
implementation 'com.rickbusarow.tangle:tangle-viewmodel-activity:0.15.0-SNAPSHOT'
// optional Compose ViewModel support
implementation 'com.rickbusarow.tangle:tangle-viewmodel-compose:0.15.0-SNAPSHOT'
// optional Fragment ViewModel support
implementation 'com.rickbusarow.tangle:tangle-viewmodel-fragment:0.15.0-SNAPSHOT'
// WorkManager
api 'com.rickbusarow.tangle:tangle-work-api:0.15.0-SNAPSHOT'
anvil 'com.rickbusarow.tangle:tangle-work-compiler:0.15.0-SNAPSHOT'
}
```
</TabItem>

</Tabs>

## Setting up the Tangle graph

In order to connect Tangle to your application-scoped Dagger component,
call `TangleGraph.add(...)` immediately after creating the component.

```kotlin
import android.app.Application
import tangle.inject.TangleGraph

class MyApplication : Application() {

override fun onCreate() {
super.onCreate()

val myAppComponent = DaggerMyAppComponent.factory()
.create(this)

TangleGraph.add(myAppComponent)
}
}
```
## Next steps

Tangle is now able to generate its code and hook in to Dagger.

Check out these features to start using Tangle in your project:
- [Fragments](fragments/fragments.mdx)
- [ViewModels](viewModels/viewModels.md)
- [WorkManager](workManager/workManager.md)
87 changes: 87 additions & 0 deletions website/versioned_docs/version-0.15.0/extending-anvil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
id: extending-anvil

sidebar_label: Extending Anvil

title: Extending Anvil
---

[Anvil] is a Kotlin compiler plugin which simplifies how we use [Dagger]. Anvil's documentation is
definitely worth a read. Its benefits can be broken down into three categories:

1. It can dramatically simplify Dagger development by automating most of the "plumbing".
2. It can **reduce build times** by [generating dagger factories], meaning that you can remove
the `dagger-compiler` kapt dependency (and probably remove kapt entirely) from most of your
project.
3. It allows us to write our own code generators. This is what Tangle does. We're able to generate
our own code, which is then integrated into the final graph by Anvil.

## Generating code from generated code

Some of the code generated by Tangle requires additional processing.

For instance, given this human-written code:

```kotlin
class MyViewModel @VMInject constructor(
@TangleParam("name")
val name: String
) : ViewModel()
```

Tangle will generate this:

```kotlin
// generated by Tangle
public class MyViewModel_Factory @Inject constructor(
internal val savedStateHandleProvider: Provider<SavedStateHandle>
) {
public fun create(): MyViewModel {
val name = savedStateHandleProvider.get().get<String>("name")
checkNotNull(name) {
"Required parameter with name `name` and type `kotlin.String` is missing from SavedStateHandle."
}
return MyViewModel(name)
}
}
```

Notice that it uses `@Inject constructor`. This code was created *after* compilation started, but it
needs to be analyzed as part of the overall Kotlin compilation task.

This is trivial if the project is using Dagger to generate dagger factories, because the kapt
compilation stage hasn't started yet. When kapt starts, it will scan the files and see the generated
MyViewModel_Factory.kt as though it's just another human-written file.

For Anvil, however, things are more difficult. The Kotlin compiler scans files once and passes that
collection of files to the Anvil plugin. So in order for Anvil to generate code for Tangle, the two
libraries need to be able to talk to one another. As Tangle creates new files, those files are
passed along to Anvil for additional processing. For the MyViewModel_Factory.kt file above, Anvil
will generate the following:

```kotlin
// generated by Anvil
public class MyViewModel_Factory_Factory(
private val param0: Provider<@JvmSuppressWildcards SavedStateHandle>
) : Factory<MyViewModel_Factory> {
public override fun `get`(): MyViewModel_Factory = newInstance(param0)

public companion object {
@JvmStatic
public fun create(param0: Provider<@JvmSuppressWildcards SavedStateHandle>):
MyViewModel_Factory_Factory = MyViewModel_Factory_Factory(param0)

@JvmStatic
public fun newInstance(param0: Provider<@JvmSuppressWildcards SavedStateHandle>):
MyViewModel_Factory = MyViewModel_Factory(param0)
}
}
```

[Anvil]: https://github.com/square/anvil

[compiler-api]: https://github.com/square/anvil/tree/main/compiler-api

[Dagger]: https://dagger.dev

[generating dagger factories]: https://github.com/square/anvil#dagger-factory-generation
Loading

0 comments on commit 9a8d4d6

Please sign in to comment.