Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align module generation and skip configuration by default #26

Merged
merged 4 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ commands:
- ~/.gradle
key: v1-dependencies-{{ checksum "build.gradle.kts" }}-{{ checksum "annotations/build.gradle.kts" }}-{{ checksum "annotations-processor/build.gradle.kts" }}-{{ checksum "common/build.gradle.kts" }}

run-unit-tests:
steps:
- run:
name: Run unit tests
command: make runUnitTests

validate-license:
steps:
- run:
Expand Down Expand Up @@ -82,6 +88,7 @@ jobs:
steps:
- checkout
- restore-gradle-cache
- run-unit-tests
- validate-license
- static-code-analysis
- validate-dokka-docs
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ bintrayPublish:
artifactoryPublish:
./gradlew :annotations:artifactoryPublish ; \
./gradlew :annotations-processor:artifactoryPublish ; \
./gradlew :common:artifactoryPublish ; \
./gradlew :common:artifactoryPublish ; \

.PHONY: runUnitTests
runUnitTests:
./gradlew examples:test

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package com.mapbox.annotation.processor

/**
* Holds information needed for configuration class generation.
* @param skipConfiguration if true, generate only package and class names for empty constructor invocation or Mapbox default module arguments injection
* @param enableConfiguration if false, generate only package and class names for empty constructor invocation,
* Kotlin object lookup or Mapbox default module arguments injection
* @param name module type parameter name
* @param simplifiedName product-based module name
*/
internal data class Module(
val skipConfiguration: Boolean,
val enableConfiguration: Boolean,
val name: String,
val simplifiedName: String,
val interfacePackage: String,
val interfaceClassName: String,
val implPackage: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
package com.mapbox.annotation.processor

import androidx.annotation.Keep
import com.google.auto.service.AutoService
import com.mapbox.annotation.*
import com.mapbox.annotation.module.MapboxModule
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.jvm.jvmStatic
import java.nio.file.Paths
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.annotation.processing.SupportedOptions
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement

@SupportedOptions(ModuleProviderGenerator.KAPT_KOTLIN_GENERATED_OPTION_NAME)
abstract class ModuleProviderGenerator(
private val moduleProviderPackage: String
) : AbstractProcessor() {
@AutoService(Processor::class)
internal class ModuleProviderGenerator : AbstractProcessor() {

override fun getSupportedSourceVersion(): SourceVersion {
return SourceVersion.latest()
}

override fun getSupportedAnnotationTypes(): MutableSet<String> {
return mutableSetOf(getAnnotationClass().name)
return mutableSetOf(MapboxModule::class.java.name)
}

internal abstract fun getAnnotationClass(): Class<Annotation>

internal abstract fun getModules(roundEnvironment: RoundEnvironment?): List<Module>

override fun process(
set: MutableSet<out TypeElement>?,
roundEnvironment: RoundEnvironment?
): Boolean {
val modules = getModules(roundEnvironment)

// look for duplicated implementations
modules.groupBy { it.interfacePackage + it.interfaceClassName }.values.forEach {
if (it.size > 1) {
val first = it[0]
modules.groupBy { it.name }.values.forEach { groupedModules ->
if (groupedModules.size > 1) {
processingEnv.messager.errorMessage {
"""
Module provider already declared for ${first.interfacePackage}.${first.interfaceClassName}
in ${first.implPackage}.${first.implClassName}.
Duplicate module declaration for ${groupedModules[0].name}
in
${groupedModules.map { "${it.implPackage}.${it.implClassName}" }}.
""".trimIndent()
}
}
Expand All @@ -53,46 +51,61 @@ abstract class ModuleProviderGenerator(
return true
}

private fun getModules(roundEnvironment: RoundEnvironment?): List<Module> {
val modules = mutableListOf<Module>()

roundEnvironment?.getElementsAnnotatedWith(MapboxModule::class.java)?.forEach { element ->
if (element is TypeElement) {
val implPackage = processingEnv.elementUtils.getPackageOf(element).toString()
val implClassName = element.simpleName.toString()

val annotation = element.getAnnotation(MapboxModule::class.java)
val enableConfiguration = annotation.enableConfiguration
val type = annotation.type

modules.add(
Module(
enableConfiguration,
type.name,
type.simplifiedName,
type.interfacePackage,
type.interfaceClassName,
implPackage,
implClassName
)
)
}
}

return modules.toList()
}

private fun generateModuleConfiguration(module: Module) {
processingEnv.messager.noteMessage { "Generating module configuration class for ${module.implPackage}.${module.implClassName}" }

val fileBuilder = FileSpec.builder(
moduleProviderPackage,
String.format(MODULE_CONFIGURATION_CLASS_NAME_FORMAT, module.name)
MODULE_PROVIDER_PACKAGE,
String.format(MODULE_CONFIGURATION_CLASS_NAME_FORMAT, module.simplifiedName)
)

val typeBuilder =
TypeSpec.objectBuilder(String.format(MODULE_CONFIGURATION_CLASS_NAME_FORMAT, module.name))
.addKdoc("Configuration provider for ${module.name} module.")
TypeSpec.objectBuilder(String.format(MODULE_CONFIGURATION_CLASS_NAME_FORMAT, module.simplifiedName))
.addKdoc("Configuration provider for ${module.simplifiedName} module.")
.addAnnotation(Keep::class)
.addProperty(
PropertySpec.builder(
MODULE_CONFIGURATION_SKIP_VARIABLE, Boolean::class)
.initializer(module.skipConfiguration.toString())
MODULE_CONFIGURATION_ENABLE_CONFIGURATION, Boolean::class)
.initializer(module.enableConfiguration.toString())
.jvmStatic()
.build()
)

if (module.skipConfiguration) {
// if configuration is skipped, generate only impl package and class paths for manual instantiation
typeBuilder.addProperty(
PropertySpec.builder(
MODULE_CONFIGURATION_SKIPPED_PACKAGE, String::class)
.initializer("\"${module.implPackage}\"")
.jvmStatic()
.build())
typeBuilder.addProperty(
PropertySpec.builder(
MODULE_CONFIGURATION_SKIPPED_CLASS, String::class)
.initializer("\"${module.implClassName}\"")
.jvmStatic()
.build())
} else {
// if not skipped, generate module instance provider field that has to be passed by the user
if (module.enableConfiguration) {
// if configuration is enabled, generate module instance provider field that has to be passed by the user
val providerInterface =
TypeSpec.interfaceBuilder(MODULE_CONFIGURATION_PROVIDER_CLASS_NAME)
.addFunction(
FunSpec.builder(String.format(MODULE_CONFIGURATION_PROVIDER_METHOD_FORMAT, module.name))
FunSpec.builder(String.format(MODULE_CONFIGURATION_PROVIDER_METHOD_FORMAT, module.simplifiedName))
.addModifiers(KModifier.ABSTRACT)
.returns(ClassName.bestGuess("${module.interfacePackage}.${module.interfaceClassName}"))
.build()
Expand All @@ -116,6 +129,20 @@ abstract class ModuleProviderGenerator(
.build()
)
.addType(providerInterface)
} else {
// if configuration is disabled, generate only impl package and class paths for manual instantiation
typeBuilder.addProperty(
PropertySpec.builder(
MODULE_CONFIGURATION_DISABLED_PACKAGE, String::class)
.initializer("\"${module.implPackage}\"")
.jvmStatic()
.build())
typeBuilder.addProperty(
PropertySpec.builder(
MODULE_CONFIGURATION_DISABLED_CLASS, String::class)
.initializer("\"${module.implClassName}\"")
.jvmStatic()
.build())
}

fileBuilder.addType(typeBuilder.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package com.mapbox.annotation.processor

import javax.annotation.processing.Messager

fun Messager.errorMessage(message: () -> String) {
internal fun Messager.errorMessage(message: () -> String) {
this.printMessage(javax.tools.Diagnostic.Kind.ERROR, message())
}

fun Messager.noteMessage(message: () -> String) {
internal fun Messager.noteMessage(message: () -> String) {
this.printMessage(javax.tools.Diagnostic.Kind.NOTE, message())
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ package com.mapbox.annotation
*/
const val MODULE_PROVIDER_PACKAGE = "com.mapbox.module"

/**
* Package that holds generated Mapbox navigation module configurations.
*/
const val MODULE_PROVIDER_PACKAGE_NAVIGATION = "$MODULE_PROVIDER_PACKAGE.navigation"

/**
* Format of the configuration class name.
*/
Expand All @@ -22,25 +17,25 @@ const val MODULE_CONFIGURATION_CLASS_NAME_FORMAT = "Mapbox_%sModuleConfiguration
/**
* Skip configuration boolean variable name.
*/
const val MODULE_CONFIGURATION_SKIP_VARIABLE = "skipConfiguration"
const val MODULE_CONFIGURATION_ENABLE_CONFIGURATION = "enableConfiguration"

/**
* When configuration is skipped, module implementation package string variable name.
* When configuration is disabled, module implementation package string variable name.
*/
const val MODULE_CONFIGURATION_SKIPPED_PACKAGE = "implPackage"
const val MODULE_CONFIGURATION_DISABLED_PACKAGE = "implPackage"

/**
* When configuration is skipped, module implementation class name string variable name.
* When configuration is disabled, module implementation class name string variable name.
*/
const val MODULE_CONFIGURATION_SKIPPED_CLASS = "implClassName"
const val MODULE_CONFIGURATION_DISABLED_CLASS = "implClassName"

/**
* When configuration is not skipepd, configuration's module provider class name.
* When configuration is enabled, configuration's module provider class name.
*/
const val MODULE_CONFIGURATION_PROVIDER_CLASS_NAME = "ModuleProvider"

/**
* When configuration is not skipepd, configuration's module provider variable name.
* When configuration is enabled, configuration's module provider variable name.
*/
const val MODULE_CONFIGURATION_PROVIDER_VARIABLE_NAME = "moduleProvider"

Expand Down
Loading