blob: 531aad807a8129b41b2a1ff37f757f7e46849804 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.internal.errors
import com.android.annotations.concurrency.Immutable
import com.android.build.gradle.internal.ide.SyncIssueImpl
import com.android.build.gradle.internal.services.ServiceRegistrationAction
import com.android.build.gradle.options.SyncOptions.EvaluationMode
import com.android.builder.errors.EvalIssueException
import com.android.builder.errors.IssueReporter
import com.android.builder.model.SyncIssue
import com.google.common.base.MoreObjects
import com.google.common.collect.ImmutableList
import com.google.common.collect.Maps
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import java.lang.UnsupportedOperationException
import java.util.concurrent.atomic.AtomicBoolean
import javax.annotation.concurrent.GuardedBy
class SyncIssueReporterImpl(
private val mode: EvaluationMode,
private val logger: Logger
) : SyncIssueReporter() {
@GuardedBy("this")
private val _syncIssues = Maps.newHashMap<SyncIssueKey, SyncIssue>()
@GuardedBy("this")
private var handlerLocked = false
@get:Synchronized
override val syncIssues: ImmutableList<SyncIssue>
get() = ImmutableList.copyOf(_syncIssues.values)
@Synchronized
override fun hasIssue(type: Type): Boolean {
return _syncIssues.values.any { issue -> issue.type == type.type }
}
@Synchronized
override fun reportIssue(
type: Type,
severity: Severity,
exception: EvalIssueException) {
val issue = SyncIssueImpl(type, severity, exception)
when (mode) {
EvaluationMode.STANDARD -> {
if (severity.severity != SyncIssue.SEVERITY_WARNING) {
throw exception
}
logger.warn("WARNING: " + exception.message)
}
EvaluationMode.IDE -> {
if (handlerLocked) {
throw IllegalStateException("Issue registered after handler locked.", exception)
}
_syncIssues[syncIssueKeyFrom(issue)] = issue
}
else -> throw RuntimeException("Unknown SyncIssue type")
}
}
@Synchronized
override fun lockHandler() {
handlerLocked = true
}
/**
* Global, build-scope, sync issue reporter. This instance can be used from build services that
* need to report sync issues, such as sdk build service.
*
* IMPORTANT: In order to avoid duplication of global build-wide sync issues, callers must
* invoke [getAllIssuesAndClear] method. This will return list of global sync issues only once,
* and any subsequent invocation will return an empty list.
*/
abstract class GlobalSyncIssueService : BuildService<GlobalSyncIssueService.Parameters>,
IssueReporter() {
interface Parameters : BuildServiceParameters {
val mode: Property<EvaluationMode>
}
private val reporter = SyncIssueReporterImpl(
parameters.mode.get(),
Logging.getLogger(GlobalSyncIssueService::class.java)
)
// Indicates if we should continue reporting issues when queried.
private val active = AtomicBoolean(true)
/**
* Returns all reported sync issues for the first invocation of the method. This is to avoid
* duplication of sync issues across project when this is queried from the model builder.
*/
fun getAllIssuesAndClear(): ImmutableList<SyncIssue> {
return if (active.compareAndSet(true, false)) {
reporter.syncIssues
} else {
ImmutableList.of()
}
}
override fun reportIssue(type: Type, severity: Severity, exception: EvalIssueException) {
reporter.reportIssue(type, severity, exception)
}
override fun hasIssue(type: Type): Boolean = reporter.hasIssue(type)
class RegistrationAction(project: Project, private val evaluationMode: EvaluationMode) :
ServiceRegistrationAction<GlobalSyncIssueService, Parameters>(
project,
GlobalSyncIssueService::class.java
) {
override fun configure(parameters: Parameters) {
parameters.mode.set(evaluationMode)
}
}
}
}
/**
* Creates a key from a SyncIssue to use in a map.
*/
private fun syncIssueKeyFrom(syncIssue: SyncIssue): SyncIssueKey {
// If data is not available we use the message part to disambiguate between issues with the
// same type.
return SyncIssueKey(syncIssue.type, syncIssue.data ?: syncIssue.message)
}
@Immutable
internal data class SyncIssueKey constructor(
private val type: Int,
private val data: String) {
override fun toString(): String {
return MoreObjects.toStringHelper(this)
.add("type", type)
.add("data", data)
.toString()
}
}