All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bancvue.gradle.multiproject.PostEvaluationNotifier.groovy Maven / Gradle / Ivy

/*
 * Copyright 2014 BancVue, LTD
 *
 * 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.bancvue.gradle.multiproject
import org.gradle.api.Project

import java.util.concurrent.atomic.AtomicInteger

/**
 * NOTE: there has to be a better way to accomplish this...
 *
 * This hack enables multi-project support for certain types of plugins.  Sometimes, a plugin needs
 * to incorporate output from sub-projects.  Code coverage is a good example - ideally, you'd want
 * a coverage report that incorporated a project and any sub-projects rather than one coverage
 * report per project in a multi-project build.  In theory, Project:evaluationDependsOnChildren()
 * could be used to support this but in practice, that does not seem to be the case.  When called
 * during plugin:apply, the sub-projects are evaluated immediately and (it seems) before the same
 * plugin is actually applied to the sub-projects.
 *
 * This hack aims to work around this issue.  A plugin can declare a static instance of this class
 * with a callback passed in during construction.  The callback is then invoked once all projects
 * and sub-projects have been evaluated.
 *
 * WARNING: if a project (or any of it's sub-projects) has already been evaluated, the
 * callback will not fire.
 */
class PostEvaluationNotifier {

	private Map projectToPreEvaluateCounterMap = [:].withDefault { new AtomicInteger(0) }
	private Closure allProjectsEvaluatedCallback

	PostEvaluationNotifier(Closure allProjectsEvaluatedCallback) {
		this.allProjectsEvaluatedCallback = allProjectsEvaluatedCallback
	}

	void addProject(Project project) {
		if (!projectToPreEvaluateCounterMap.containsKey(project)) {
			projectToPreEvaluateCounterMap.get(project).incrementAndGet()

			project.afterEvaluate {
				afterEvaluate(project)
			}

			project.subprojects.each { Project subProject ->
				addProject(subProject)
			}
		}
	}

	private void afterEvaluate(Project project) {
		projectToPreEvaluateCounterMap.get(project).decrementAndGet()

		if (allProjectsEvaluated()) {
			List allUniqueProjects = projectToPreEvaluateCounterMap.keySet() as List
			invokeCallbackOnAllProjects(allUniqueProjects)
			projectToPreEvaluateCounterMap.clear()
		}
	}

	private boolean allProjectsEvaluated() {
		AtomicInteger nonZeroCounter = projectToPreEvaluateCounterMap.values().find { AtomicInteger counter ->
			counter.get() != 0
		}
		nonZeroCounter == null
	}

	private void invokeCallbackOnAllProjects(List allUniqueProjects) {
		if (doesCallbackAcceptSingleProjectAsParameter()) {
			allUniqueProjects.each { Project project ->
				allProjectsEvaluatedCallback.call(project)
			}
		} else {
			allProjectsEvaluatedCallback.call(allUniqueProjects)
		}
	}

	private boolean doesCallbackAcceptSingleProjectAsParameter() {
		Class[] parameterTypes = allProjectsEvaluatedCallback.getParameterTypes()
		(parameterTypes.length == 1) && (parameterTypes[0] == Project)
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy