-
Notifications
You must be signed in to change notification settings - Fork 90
New input format proposition #36
base: sharing-resources-between-android-modules
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.google.androidstudiopoet.input | ||
|
||
class AndroidBuildConfig { | ||
var minSdkVersion: Int? = null | ||
var compileSdkVersion: Int? = null | ||
var targetSdkVersion: Int? = null | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.google.androidstudiopoet.input | ||
|
||
/** | ||
* Configuration of the build system, right now only Gradle is supported | ||
*/ | ||
class BuildSystemConfig { | ||
/** | ||
* Build system version | ||
*/ | ||
var version: String? = null | ||
|
||
/** | ||
* AGP version | ||
*/ | ||
var agpVersion: String? = null | ||
|
||
/** | ||
* Kotlin version | ||
*/ | ||
var kotlinVersion: String? = null | ||
|
||
|
||
/** | ||
* Default AndroidBuildConfig for all android modules | ||
*/ | ||
var defaultAndroidBuildConfig: AndroidBuildConfig? = null | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.google.androidstudiopoet.input | ||
|
||
open class DependencyConfig { | ||
|
||
var type: DependencyType? = null | ||
|
||
/** | ||
* Identifies ModuleConfig that this one should depend on | ||
*/ | ||
lateinit var moduleNamePrefix: String | ||
|
||
} | ||
|
||
enum class DependencyType { | ||
SINGLE, | ||
BULK | ||
} | ||
|
||
class BulkDependencyConfig: DependencyConfig() { | ||
/** | ||
* Amount of modules from target bulk to depend on. | ||
* If ModuleConfig with ModuleConfig#moduleCount >= 1 contains this BulkDependencyConfig then algorithm will try to | ||
* distribute dependencies evenly, and do its' best to be sure that there are dependencies on the all modules from | ||
* the target bulk | ||
*/ | ||
var moduleCount: Int = 1 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.google.androidstudiopoet.input | ||
|
||
class Flavour { | ||
var name: String? = null | ||
var dimension: String? = null | ||
var buildConfig: AndroidBuildConfig? = null | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.google.androidstudiopoet.input | ||
|
||
class GenerationConfig { | ||
lateinit var inputVersion: String | ||
|
||
lateinit var projectConfig: ProjectConfig | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.google.androidstudiopoet.input | ||
|
||
enum class ModuleType { | ||
ANDROID, | ||
PURE | ||
} | ||
|
||
open class ModuleConfig { | ||
|
||
lateinit var moduleType: ModuleType | ||
|
||
/** | ||
* How many modules should be created according to this configuration | ||
*/ | ||
var moduleCount: Int? = null | ||
|
||
/** | ||
* Module Name prefix used to create an actual modules names according to formula "$moduleNamePrefix$moduleNumber", | ||
* So if moduleCount = 2 and moduleNamePrefix = "module" then there would be generated two modules with names "module0" and "module1" | ||
* All the module names in the project must be unique | ||
*/ | ||
lateinit var moduleNamePrefix: String | ||
|
||
/** | ||
* how many java packages should be generated | ||
*/ | ||
var javaPackageCount: Int = 1 | ||
|
||
/** | ||
* how many kotlin packages should be generated | ||
*/ | ||
var kotlinPackageCount: Int = 1 | ||
|
||
/** | ||
* how many classes should be generated in each Java package | ||
*/ | ||
var javaClassCount: Int = 1 | ||
|
||
/** | ||
* how many classes should be generated in each Kotlin package | ||
*/ | ||
var kotlinClassCount: Int = 1 | ||
|
||
/** | ||
* how many methods should be generated in each Java class | ||
*/ | ||
var javaMethodCount: Int = 1 | ||
|
||
/** | ||
* how many methods should be generated in each Kotlin class | ||
*/ | ||
var kotlinMethodCount: Int = 1 | ||
|
||
var dependencies: List<DependencyConfig>? = listOf() | ||
} | ||
|
||
class AndroidModuleConfig: ModuleConfig() { | ||
/** | ||
* If true then android application plugin is applied. | ||
*/ | ||
val isApplication: Boolean? = false | ||
val buildConfig: AndroidBuildConfig? = null | ||
|
||
val numActivities: Int? = 0 | ||
val resourcesConfig: ResourcesConfig? = null | ||
|
||
val flavours: List<Flavour>? = null | ||
} | ||
|
||
class ResourcesConfig { | ||
var stringCount: Int? = null | ||
var imageCount: Int? = null | ||
var layoutCount: Int? = null | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
{ | ||
//Project configuration is wrapped in another object to help us deal with versioning | ||
"inputVersion": "0.0.1", | ||
"projectConfig": { | ||
"projectName": "genny", | ||
"root": "./modules/", | ||
"buildSystemConfig": { | ||
//Optional param, but it is possible to specify what exactly build system setup should be used | ||
}, | ||
"moduleConfigs": [ | ||
//Even though there are only 3 blocks here, in total 11 modules would be generated. | ||
//One other thing, I believe that we should forbid dependencies withing one block, only interblock dependencies are allowed. | ||
{ | ||
//Andorid Application Module | ||
"moduleType": "ANDROID", | ||
"moduleNamePrefix": "app", | ||
"isApplication": true, | ||
|
||
"numActivities": 8, | ||
"resourcesConfig": { | ||
"stringCount": 10, | ||
"imageCount": 9, | ||
"layoutCount": 7 | ||
}, | ||
|
||
"javaPackageCount": 3, | ||
"javaClassCount": 8, | ||
"javaMethodCount": 2000, | ||
|
||
"kotlinPackageCount": 3, | ||
"kotlinClassCount": 5, | ||
"kotlinMethodCount": 1000, | ||
|
||
"dependencies": [ | ||
{ | ||
//Depends on the first "pure" module | ||
"type": "SINGLE", | ||
"moduleNamePrefix": "pure" | ||
}, | ||
{ | ||
//Depends on the all five "andoirdLib" modules | ||
"type": "SINGLE", | ||
"moduleNamePrefix": "androidLib", | ||
"moduleCount": 5 | ||
} | ||
], | ||
"flavours": [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible to also support input flavors as done in 917229b, that way helps when adding many flavors. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem with that approach is that amount of flavour*dimens grows like n^2. We can think about adding "bulk" flavour/dimen generation like it is done with packages, would that work for you? Then there'll just be multiple types of FlavourConfig There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the number of variants is the product of the number of flavors per dimension, it grows really fast (exponentially assuming each dimension has the same number of flavors). Adding bulk as in packages can also work (having a prefix, a dimension and a counter). Another way is by adding an optional counter, similar to what is done with the modules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I meant modules, not packages. I'll think about it and push to this branch want I'll come up with. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that having two counters instead of one is more practical, one for the number of flavours the other for dimensions, then it would be more flexible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Every dimension can have a different number of flavors. What the input would look like with what I said is something like this: "flavours": [ This would generate 7 flavors ("red0" to "red4", "green0" and "green1") for dimension "color" and 2 for "license" (the total number of variants in this case would be 28 ( 7 in color x 2 in license x {debug | release} ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this the format is perfect. We can make it a tiny little bit more flexible. We can add one more param "dimensionCounter", then we can easily create tons of dimensions with tons of flavours. Otherwise we would be forced to write one block for each dimension |
||
{ | ||
"name": "master" | ||
}, | ||
{ | ||
"name": "Dev21", | ||
"buildConfig": { | ||
"minSdkVersion": 21 | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
//5 Pure Java + Kotlin libraries | ||
"moduleType": "PURE", | ||
"moduleCount": 5, | ||
"moduleNamePrefix": "pure", | ||
|
||
"javaPackageCount": 3, | ||
"javaClassCount": 8, | ||
"javaMethodCount": 2000, | ||
|
||
"kotlinPackageCount": 3, | ||
"kotlinClassCount": 5, | ||
"kotlinMethodCount": 1000 | ||
}, | ||
{ | ||
//5 Android libraries that depends on corresponding "pure" modules | ||
"moduleType": "ANDROID", | ||
"moduleCount": 5, | ||
"moduleNamePrefix": "androidLib", | ||
|
||
"numActivities": 2, | ||
"resourcesConfig": { | ||
"stringCount": 1, | ||
"imageCount": 5, | ||
"layoutCount": 3 | ||
}, | ||
|
||
"javaPackageCount": 3, | ||
"javaClassCount": 8, | ||
"javaMethodCount": 2000, | ||
|
||
"kotlinPackageCount": 3, | ||
"kotlinClassCount": 5, | ||
"kotlinMethodCount": 1000, | ||
"dependencies": [{ | ||
//Each "androidLib" depends on one "pure" module, so that they together depends on as many modules as possible | ||
"type": "BULK", | ||
"moduleNamePrefix": "pure", | ||
"moduleCount": 1 | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.google.androidstudiopoet.input | ||
|
||
/** | ||
* Configuration of the whole project that should be generated | ||
*/ | ||
class ProjectConfig { | ||
|
||
|
||
/** | ||
* Name of the new project | ||
*/ | ||
lateinit var projectName: String | ||
|
||
/** | ||
* Directory where generator should put generated project | ||
*/ | ||
lateinit var root: String | ||
|
||
/** | ||
* Name of the buildSystemConfig | ||
*/ | ||
var buildSystemConfig: BuildSystemConfig? = null | ||
|
||
/** | ||
* Module Configurations | ||
*/ | ||
lateinit var moduleConfigs: List<ModuleConfig> | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think on also having explicit dependencies outside ModuleConfigs? Following this structure:
class ExplicitDependency {
val from : List
val to : List
val type : String?
}
The format accepted by "from" and "to" would be a list of strings, each representing a prefix (meaning all the modules of that ModuleConfig) or a specific module.
An example is like this:
"dependencies": [{"from": ["prefixA", "prefixB5"], "to": ["prefixC", "prefixB1"]}, {"from": ["prefixA"], "to": ["prefixB0"], "type": "api"}]
That would create the following:
We can let the type for later since that has other implications (for example, how the code dependencies should be generated). This format (a list of prefixes and modules) can be used in other places to describe a set of modules and can be reused in topologies.
While thinking on how input processing will be implemented, at the end, all of this will be used to generate a list of explicit module names that will be added to the build.gradle files. I think both formats can easily be adapted to use the same structure that will hold those strings. Right now it is a List<Dependency> that contains pairs of integers. This structure needs to change to support dependencies between different ModuleConfig. What do you think of changing this list to Map<String, List<Dependency>>? The key will be the module name while the list holds the modules it depends on. We can also change the structure of Dependency since now the "from" will be redundant and also add the dependency type ("api", "implementation", etc.).
One advantage of using a map instead of a list is that we will not require to filter on every module since the map will give us that in constant time, also improving performance (in the worst case, this list can have N*(N-1)/2 dependencies, causing the process of adding dependencies into build files to be cubic on the number of modules, while using a map is quadratic).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is true, but it will change completely with the new format, each
ModuleConfig
will have a List where it refers other ModuleConfig by prefix. https://github.com/NikitaKozlov/android-studio-poet/blob/71be1842b8c112d3e979ae083926df09a3dfef1d/src/main/kotlin/com/google/androidstudiopoet/input/DependencyConfig.kt#L3It is possible to express with new format everything you listed ("api" and "implementation" is easy to add), although it will be scattered through different
ModuleConfig
s:Because each
ModuleConfig
explicitly shows what otherModuleConfig
s it depends on then there'll be no more filtering.I think we can first implement one way of doing things and after we play with it we can reevaluate it. If it would be too verbose, or it wouldn't fit some of the needs (for example topologies) we can add
ExplicitDependency
later.I'm not sure how do you want to use
ExplicitDependency
for topologies, can you please explain it?Unfortunately module name is not enough for generating dependency, we also need method that needs to be called and resources that need to be refered, currently figuring out those things could be computationally expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please explain as well the problem you are trying to solve? Then it would be easier for me to agree with you or offer some alternatives.