Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@
"depNameTemplate": "androidx.hilt:hilt-navigation-compose",
"datasourceTemplate": "maven"
},
{
"fileMatch": ["^.*Versions\\.kt$"],
"matchStrings": ["val metro: String = \"(?<currentValue>\\d+\\.\\d+\\.\\d+)\""],
"depNameTemplate": "dev.zacsweers.metro:dev.zacsweers.metro.gradle.plugin",
"datasourceTemplate": "maven"
},
{
"fileMatch": ["^.*Versions\\.kt$"],
"matchStrings": ["val composeBom: String = \"(?<currentValue>\\d+\\.\\d+\\.\\d+)\""],
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Then, you can use the versions.yaml in the `generate-project` command:
- `--layers`: Number of layers (default: 5)
- `--language`: kts (default), groovy, both
- `--type`: android (default), jvm
- `--di`: hilt (default), metro, none
- `--classes-module`: Number of classes per module (default: 5)
- `--classes-module-type`: fixed (default), random
- `--type-of-string-resources`: normal (default), large
Expand All @@ -66,7 +67,7 @@ ProjectGenerator(
language = Language.KTS,
typeOfProjectRequested = TypeProjectRequested.ANDROID,
classesPerModule = ClassesPerModule(ClassesPerModuleType.FIXED, 20),
versions = Versions(project = Project(jdk = "17")),
versions = Versions(project = Project(jdk = "17"), di = DependencyInjection.HILT),
typeOfStringResources = TypeOfStringResources.LARGE,
layers = 5,
generateUnitTest = true,
Expand Down
2 changes: 0 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ plugins {
alias(libs.plugins.ktlint)
}


project.extensions.getByType(KotlinJvmProjectExtension::class.java).apply {
jvmToolchain(23)
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class GenerateVersionsYaml {
| develocity: ${versions.project.develocity}
| develocityUrl: ${versions.project.develocityUrl}
| jdk: ${versions.project.jdk}
|di: ${versions.di}
|kotlin:
| kgp: ${versions.kotlin.kgp}
| ksp: ${versions.kotlin.ksp}
Expand All @@ -31,6 +32,8 @@ class GenerateVersionsYaml {
| work: ${versions.android.work}
| hilt: ${versions.android.hilt}
| hiltAandroidx: ${versions.android.hiltAandroidx}
| metro: ${versions.android.metro}
| metroPlugin: ${versions.android.metroPlugin}
| composeBom: ${versions.android.composeBom}
|testing:
| junit4: ${versions.testing.junit4}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class GenerateProjects : CliktCommand(name = "generate-project") {
private val modules by option().int().required()
.check("max number of projects 4000") { it in (layers + 1)..4000 }
private val type by option().choice("android", "jvm").default("android")
private val di: String by option().choice("hilt", "metro", "none").default("hilt")
private val classesModule by option().int().default(5)
private val classesModuleType: String by option().choice("fixed", "random").default("fixed")
private val typeOfStringResources: String by option().choice("large", "normal").default("normal")
Expand All @@ -62,7 +63,8 @@ class GenerateProjects : CliktCommand(name = "generate-project") {
override fun run() {
val typeOfProjectRequested = TypeProjectRequested.valueOf(type.uppercase())
val shape = Shape.valueOf(shape.uppercase())
val versions = getVersions(versionsFile, develocityUrl, agp9)
val dependencyInjection = DependencyInjection.valueOf(di.uppercase())
val versions = getVersions(versionsFile, develocityUrl, agp9).copy(di = dependencyInjection)
val develocityEnabled = getDevelocityEnabled(develocity, develocityUrl)
ProjectGenerator(
modules,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package io.github.cdsap.projectgenerator.generator.android

import io.github.cdsap.projectgenerator.model.DependencyInjection
import io.github.cdsap.projectgenerator.model.LanguageAttributes
import io.github.cdsap.projectgenerator.model.ProjectGraph
import io.github.cdsap.projectgenerator.NameMappings
import io.github.cdsap.projectgenerator.generator.classes.GenerateDictionaryAndroid
import io.github.cdsap.projectgenerator.model.ClassTypeAndroid
import java.io.File
import java.util.concurrent.CopyOnWriteArrayList

class AndroidApplication {
fun createApplicationClass(node: ProjectGraph, lang: LanguageAttributes) {
fun createApplicationClass(
node: ProjectGraph,
lang: LanguageAttributes,
di: DependencyInjection,
dictionary: MutableMap<String, CopyOnWriteArrayList<GenerateDictionaryAndroid>>
) {
val layerDir = NameMappings.layerName(node.layer)
val moduleDir = NameMappings.moduleName(node.id)
val packageDir = NameMappings.modulePackageName(node.id)
Expand All @@ -15,60 +24,94 @@ class AndroidApplication {
appDir.mkdirs()
val appFile = File(appDir, "MainApplication.kt")

// Create a more robust Application class that properly initializes Hilt
val appContent = """
|package com.awesomeapp.$packageDir
|
|import android.app.Application
|import android.content.Context
|import androidx.hilt.work.HiltWorkerFactory
|import androidx.work.Configuration
|import dagger.hilt.android.HiltAndroidApp
|import dagger.hilt.android.qualifiers.ApplicationContext
|import javax.inject.Inject
|
|/**
| * Main application class with Hilt support
| */
|@HiltAndroidApp
|class MainApplication : Application(){
|
| override fun onCreate() {
| super.onCreate()
| // Application initialization here
| }
|
| companion object {
| /**
| * Gets an entry point from the Hilt component
| */
| @JvmStatic
| fun getEntryPoint(@ApplicationContext context: Context, entryPoint: Class<*>): Any {
| return try {
| val entryPointAccessors = Class.forName("dagger.hilt.android.EntryPointAccessors")
| val getMethod = entryPointAccessors.getDeclaredMethod("fromApplication", Context::class.java, Class::class.java)
| getMethod.invoke(null, context.applicationContext, entryPoint)
| } catch (e: Exception) {
| throw RuntimeException("Error accessing Hilt entry point", e)
| }
| }
|
| /**
| * Gets a specific implementation from an entry point
| */
| @JvmStatic
| inline fun <reified T> getImplementation(@ApplicationContext context: Context, entryPoint: Class<*>): T {
| val ep = getEntryPoint(context, entryPoint)
| // Find method that returns T
| val method = entryPoint.methods.find {
| it.returnType == T::class.java
| } ?: throw RuntimeException("No method found returning")
|
| return method.invoke(ep) as T
| }
| }
|}
""".trimMargin()
val appContent = when (di) {
DependencyInjection.HILT -> """
|package com.awesomeapp.$packageDir
|
|import android.app.Application
|import android.content.Context
|import androidx.hilt.work.HiltWorkerFactory
|import androidx.work.Configuration
|import dagger.hilt.android.HiltAndroidApp
|import dagger.hilt.android.qualifiers.ApplicationContext
|import javax.inject.Inject
|
|/**
| * Main application class with Hilt support
| */
|@HiltAndroidApp
|class MainApplication : Application(){
|
| override fun onCreate() {
| super.onCreate()
| // Application initialization here
| }
|
| companion object {
| /**
| * Gets an entry point from the Hilt component
| */
| @JvmStatic
| fun getEntryPoint(@ApplicationContext context: Context, entryPoint: Class<*>): Any {
| return try {
| val entryPointAccessors = Class.forName("dagger.hilt.android.EntryPointAccessors")
| val getMethod = entryPointAccessors.getDeclaredMethod("fromApplication", Context::class.java, Class::class.java)
| getMethod.invoke(null, context.applicationContext, entryPoint)
| } catch (e: Exception) {
| throw RuntimeException("Error accessing Hilt entry point", e)
| }
| }
|
| /**
| * Gets a specific implementation from an entry point
| */
| @JvmStatic
| inline fun <reified T> getImplementation(@ApplicationContext context: Context, entryPoint: Class<*>): T {
| val ep = getEntryPoint(context, entryPoint)
| // Find method that returns T
| val method = entryPoint.methods.find {
| it.returnType == T::class.java
| } ?: throw RuntimeException("No method found returning")
|
| return method.invoke(ep) as T
| }
| }
|}
""".trimMargin()

DependencyInjection.METRO -> {
val moduleId = NameMappings.moduleName(node.id)
val moduleNumber = node.id.split("_").last().toInt()
val viewModel = dictionary[moduleId]
?.firstOrNull { it.type == ClassTypeAndroid.VIEWMODEL }
?.className
?: "Viewmodel${moduleNumber}_1"
"""
|package com.awesomeapp.$packageDir
|
|import android.app.Application
|import dev.zacsweers.metro.DependencyGraph
|import dev.zacsweers.metro.createGraph
|
|@DependencyGraph
|interface AppGraph {
| val viewModel: $viewModel
|}
|
|class MainApplication : Application() {
| val graph: AppGraph by lazy { createGraph() }
|}
""".trimMargin()
}

DependencyInjection.NONE -> """
|package com.awesomeapp.$packageDir
|
|import android.app.Application
|
|class MainApplication : Application()
""".trimMargin()
}
appFile.writeText(appContent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import io.github.cdsap.projectgenerator.model.LanguageAttributes
import io.github.cdsap.projectgenerator.model.ProjectGraph
import io.github.cdsap.projectgenerator.model.TypeProject
import io.github.cdsap.projectgenerator.model.Versions
import io.github.cdsap.projectgenerator.model.DependencyInjection
import io.github.cdsap.projectgenerator.NameMappings
import java.io.File

class BuildFilesGeneratorAndroid(val versions: Versions) : BuildFilesGenerator {
class BuildFilesGeneratorAndroid(
private val versions: Versions,
private val di: DependencyInjection
) : BuildFilesGenerator {
override fun generateBuildFiles(
node: ProjectGraph,
lang: LanguageAttributes,
Expand Down Expand Up @@ -37,7 +41,7 @@ class BuildFilesGeneratorAndroid(val versions: Versions) : BuildFilesGenerator {
}
}

val deps = AndroidToml().tomlImplementations(versions)
val deps = AndroidToml().tomlImplementations(versions, di)
return """
|plugins {
| id("awesome.androidapp.plugin")
Expand All @@ -55,7 +59,7 @@ ${testImplementations.joinToString("\n").prependIndent(" ")}
private fun createAndroidLibBuildFile(node: ProjectGraph, generateUnitTests: Boolean): String {
val implementations = mutableSetOf<String>()
val testImplementations = mutableSetOf<String>()
val deps = AndroidToml().tomlImplementations(versions)
val deps = AndroidToml().tomlImplementations(versions, di)
// Add direct dependencies first (only from different layers)
node.nodes.forEach { dependency ->
if (dependency.layer != node.layer) {
Expand Down
Loading