| /* |
| * 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() |
| } |
| } |