/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.gradle.plugins.defaults; import com.liferay.gradle.plugins.LiferayThemePlugin; import com.liferay.gradle.plugins.cache.WriteDigestTask; import com.liferay.gradle.plugins.defaults.extensions.LiferayThemeDefaultsExtension; import com.liferay.gradle.plugins.defaults.internal.LiferayRelengPlugin; import com.liferay.gradle.plugins.defaults.internal.util.FileUtil; import com.liferay.gradle.plugins.defaults.internal.util.GradleUtil; import com.liferay.gradle.plugins.defaults.internal.util.IncrementVersionClosure; import com.liferay.gradle.plugins.defaults.tasks.ReplaceRegexTask; import com.liferay.gradle.plugins.extensions.LiferayExtension; import com.liferay.gradle.plugins.gulp.ExecuteGulpTask; import com.liferay.gradle.plugins.node.tasks.PublishNodeModuleTask; import com.liferay.gradle.plugins.util.PortalTools; import com.liferay.gradle.util.copy.StripPathSegmentsAction; import groovy.lang.Closure; import java.io.File; import java.util.Properties; import java.util.concurrent.Callable; import org.gradle.api.Action; import org.gradle.api.GradleException; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.DependencySet; import org.gradle.api.file.FileTree; import org.gradle.api.logging.Logger; import org.gradle.api.plugins.BasePlugin; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.MavenPlugin; import org.gradle.api.tasks.Copy; import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.Upload; import org.gradle.api.tasks.bundling.Zip; import org.gradle.util.GUtil; /** * @author Andrea Di Giorgi */ public class LiferayThemeDefaultsPlugin implements Plugin<Project> { public static final String EXPAND_FRONTEND_CSS_COMMON_TASK_NAME = "expandFrontendCSSCommon"; public static final String FRONTEND_CSS_COMMON_CONFIGURATION_NAME = "frontendCSSCommon"; public static final String PLUGIN_NAME = "liferayThemeDefaults"; public static final String PUBLISH_NODE_MODULE_TASK_NAME = "publishNodeModule"; public static final String WRITE_PARENT_THEMES_DIGEST_TASK_NAME = "writeParentThemesDigest"; public static final String ZIP_RESOURCES_IMPORTER_ARCHIVES_TASK_NAME = "zipResourcesImporterArchives"; @Override public void apply(Project project) { GradleUtil.applyPlugin(project, LiferayThemePlugin.class); _applyPlugins(project); // GRADLE-2427 _addTaskInstall(project); _applyConfigScripts(project); final LiferayThemeDefaultsExtension liferayThemeDefaultsExtension = GradleUtil.addExtension( project, PLUGIN_NAME, LiferayThemeDefaultsExtension.class); LiferayOSGiDefaultsPlugin.configureRepositories(project); Configuration frontendCSSCommonConfiguration = _addConfigurationFrontendCSSCommon(project); final Project frontendThemeStyledProject = _getThemeProject( project, "frontend-theme-styled"); final Project frontendThemeUnstyledProject = _getThemeProject( project, "frontend-theme-unstyled"); final WriteDigestTask writeDigestTask = _addTaskWriteParentThemesDigest( project, frontendThemeStyledProject, frontendThemeUnstyledProject); final Copy expandFrontendCSSCommonTask = _addTaskExpandFrontendCSSCommon( project, frontendCSSCommonConfiguration); final ReplaceRegexTask updateVersionTask = _addTaskUpdateVersion( project, writeDigestTask); File resourcesImporterArchivesDir = project.file( "src/WEB-INF/src/resources-importer"); File resourcesImporterExpandedArchivesDir = project.file( "resources-importer"); Task zipResourcesImporterArchivesTask = _addTaskZipDirectories( project, ZIP_RESOURCES_IMPORTER_ARCHIVES_TASK_NAME, resourcesImporterExpandedArchivesDir, resourcesImporterArchivesDir, "lar"); final PublishNodeModuleTask publishNodeModuleTask = _addTaskPublishNodeModule(zipResourcesImporterArchivesTask); _configureDeployDir(project); _configureProject(project); _configureTasksExecuteGulp(project, zipResourcesImporterArchivesTask); GradleUtil.excludeTasksWithProperty( project, LiferayOSGiDefaultsPlugin.SNAPSHOT_IF_STALE_PROPERTY_NAME, true, MavenPlugin.INSTALL_TASK_NAME, BasePlugin.UPLOAD_ARCHIVES_TASK_NAME); project.afterEvaluate( new Action<Project>() { @Override public void execute(Project project) { if (liferayThemeDefaultsExtension. isUseLocalDependencies()) { _configureTasksExecuteGulpLocalDependencies( project, expandFrontendCSSCommonTask, frontendThemeStyledProject, frontendThemeUnstyledProject); } else { writeDigestTask.setEnabled(false); } GradleUtil.setProjectSnapshotVersion(project); // setProjectSnapshotVersion must be called before // configureTaskUploadArchives, because the latter one needs // to know if we are publishing a snapshot or not. _configureTaskUploadArchives( project, publishNodeModuleTask, updateVersionTask); } }); } private Configuration _addConfigurationFrontendCSSCommon( final Project project) { Configuration configuration = GradleUtil.addConfiguration( project, FRONTEND_CSS_COMMON_CONFIGURATION_NAME); configuration.defaultDependencies( new Action<DependencySet>() { @Override public void execute(DependencySet dependencySet) { _addDependenciesFrontendCSSCommon(project); } }); configuration.setDescription( "Configures com.liferay.frontend.css.common for compiling the " + "theme."); configuration.setTransitive(false); configuration.setVisible(false); return configuration; } private void _addDependenciesFrontendCSSCommon(Project project) { String version = PortalTools.getVersion( project, _FRONTEND_COMMON_CSS_NAME); GradleUtil.addDependency( project, FRONTEND_CSS_COMMON_CONFIGURATION_NAME, "com.liferay", _FRONTEND_COMMON_CSS_NAME, version, false); } private Copy _addTaskExpandFrontendCSSCommon( final Project project, final Configuration frontendCSSCommonConfguration) { Copy copy = GradleUtil.addTask( project, EXPAND_FRONTEND_CSS_COMMON_TASK_NAME, Copy.class); copy.doFirst( new Action<Task>() { @Override public void execute(Task task) { Copy copy = (Copy)task; project.delete(copy.getDestinationDir()); } }); copy.eachFile(new StripPathSegmentsAction(2)); copy.from( new Closure<Void>(project) { @SuppressWarnings("unused") public FileTree doCall() { return project.zipTree( frontendCSSCommonConfguration.getSingleFile()); } }); copy.include("META-INF/resources/"); copy.into(new File(project.getBuildDir(), "frontend-css-common")); copy.setDescription( "Expands com.liferay.frontend.css.common to a temporary " + "directory."); copy.setIncludeEmptyDirs(false); return copy; } private Upload _addTaskInstall(Project project) { Upload upload = GradleUtil.addTask( project, MavenPlugin.INSTALL_TASK_NAME, Upload.class); Configuration configuration = GradleUtil.getConfiguration( project, Dependency.ARCHIVES_CONFIGURATION); upload.setConfiguration(configuration); upload.setDescription( "Installs the '" + configuration.getName() + "' artifacts into the local Maven repository."); return upload; } private PublishNodeModuleTask _addTaskPublishNodeModule( Task zipResourcesImporterArchivesTask) { Project project = zipResourcesImporterArchivesTask.getProject(); String projectPath = project.getPath(); if (projectPath.startsWith(":private:")) { return null; } PublishNodeModuleTask publishNodeModuleTask = GradleUtil.addTask( project, PUBLISH_NODE_MODULE_TASK_NAME, PublishNodeModuleTask.class); publishNodeModuleTask.dependsOn(zipResourcesImporterArchivesTask); publishNodeModuleTask.setDescription( "Publishes this project to the NPM registry."); publishNodeModuleTask.setGroup(BasePlugin.UPLOAD_GROUP); return publishNodeModuleTask; } private ReplaceRegexTask _addTaskUpdateVersion( Project project, WriteDigestTask writeParentThemesDigestTask) { ReplaceRegexTask replaceRegexTask = GradleUtil.addTask( project, LiferayRelengPlugin.UPDATE_VERSION_TASK_NAME, ReplaceRegexTask.class); replaceRegexTask.finalizedBy(writeParentThemesDigestTask); File npmShrinkwrapJsonFile = project.file("npm-shrinkwrap.json"); if (npmShrinkwrapJsonFile.exists()) { replaceRegexTask.match(_JSON_VERSION_REGEX, npmShrinkwrapJsonFile); } replaceRegexTask.match(_JSON_VERSION_REGEX, "package.json"); replaceRegexTask.setDescription( "Updates the project version in the package.json file."); replaceRegexTask.setReplacement( IncrementVersionClosure.MICRO_INCREMENT); return replaceRegexTask; } private WriteDigestTask _addTaskWriteParentThemesDigest( Project project, Project... parentThemeProjects) { WriteDigestTask writeDigestTask = GradleUtil.addTask( project, WRITE_PARENT_THEMES_DIGEST_TASK_NAME, WriteDigestTask.class); writeDigestTask.setDescription( "Writes a digest file to keep track of the parent themes used by " + "this project."); for (Project parentThemeProject : parentThemeProjects) { if (parentThemeProject == null) { continue; } writeDigestTask.dependsOn( parentThemeProject.getPath() + ":" + JavaPlugin.CLASSES_TASK_NAME); File dir = parentThemeProject.file( "src/main/resources/META-INF/resources"); writeDigestTask.source(dir); } return writeDigestTask; } private Task _addTaskZipDirectories( Project project, String taskName, File rootDir, File destinationDir, String extension) { Task task = project.task(taskName); StringBuilder sb = new StringBuilder(); sb.append("Assembles "); sb.append(extension.toUpperCase()); sb.append(" files in "); sb.append(project.relativePath(destinationDir)); sb.append(" from the subdirectories of "); sb.append(project.relativePath(rootDir)); sb.append('.'); task.setDescription(sb.toString()); File[] dirs = FileUtil.getDirectories(rootDir); if (dirs != null) { for (File dir : dirs) { Zip zip = _addTaskZipDirectory( project, GradleUtil.getTaskName(taskName, dir), dir, destinationDir, extension); task.dependsOn(zip); } } return task; } private Zip _addTaskZipDirectory( Project project, String taskName, File dir, File destinationDir, String extension) { Zip zip = GradleUtil.addTask(project, taskName, Zip.class); zip.from(dir); zip.setArchiveName(dir.getName() + "." + extension); zip.setDestinationDir(destinationDir); zip.setDescription( "Assembles " + project.relativePath(zip.getArchivePath()) + " with the contents of the " + project.relativePath(dir) + " directory."); return zip; } private void _applyConfigScripts(Project project) { GradleUtil.applyScript( project, "com/liferay/gradle/plugins/defaults/dependencies" + "/config-maven.gradle", project); } private void _applyPlugins(Project project) { GradleUtil.applyPlugin(project, MavenPlugin.class); } private void _configureDeployDir(Project project) { final LiferayExtension liferayExtension = GradleUtil.getExtension( project, LiferayExtension.class); boolean requiredForStartup = _getPluginPackageProperty( project, "required-for-startup"); if (requiredForStartup) { liferayExtension.setDeployDir( new Callable<File>() { @Override public File call() throws Exception { return new File( liferayExtension.getLiferayHome(), "osgi/war"); } }); } else { liferayExtension.setDeployDir( new Callable<File>() { @Override public File call() throws Exception { return new File( liferayExtension.getLiferayHome(), "deploy"); } }); } } private void _configureProject(Project project) { String group = GradleUtil.getGradlePropertiesValue( project, "project.group", _GROUP); project.setGroup(group); } private void _configureTaskExecuteGulp( ExecuteGulpTask executeGulpTask, Task zipResourcesImporterArchivesTask) { executeGulpTask.args("--skip-update-check=true"); executeGulpTask.dependsOn(zipResourcesImporterArchivesTask); } private void _configureTaskExecuteGulpLocalDependencies( ExecuteGulpTask executeGulpTask, Copy expandFrontendCSSCommonTask, Project frontendThemeStyledProject, Project frontendThemeUnstyledProject) { File cssCommonDir = expandFrontendCSSCommonTask.getDestinationDir(); executeGulpTask.args( "--css-common-path=" + FileUtil.getAbsolutePath(cssCommonDir)); executeGulpTask.dependsOn(expandFrontendCSSCommonTask); _configureTaskExecuteGulpLocalDependenciesTheme( executeGulpTask, frontendThemeStyledProject, "styled"); _configureTaskExecuteGulpLocalDependenciesTheme( executeGulpTask, frontendThemeUnstyledProject, "unstyled"); } private void _configureTaskExecuteGulpLocalDependenciesTheme( ExecuteGulpTask executeGulpTask, Project themeProject, String name) { if (themeProject == null) { Project project = executeGulpTask.getProject(); Logger logger = project.getLogger(); if (logger.isWarnEnabled()) { logger.warn("Unable to configure {} parent theme", name); } return; } File dir = themeProject.file( "src/main/resources/META-INF/resources/_" + name); executeGulpTask.args( "--" + name + "-path=" + FileUtil.getAbsolutePath(dir)); executeGulpTask.dependsOn( themeProject.getPath() + ":" + JavaPlugin.CLASSES_TASK_NAME); } private void _configureTasksExecuteGulp( Project project, final Task zipResourcesImporterArchivesTask) { TaskContainer taskContainer = project.getTasks(); taskContainer.withType( ExecuteGulpTask.class, new Action<ExecuteGulpTask>() { @Override public void execute(ExecuteGulpTask executeGulpTask) { _configureTaskExecuteGulp( executeGulpTask, zipResourcesImporterArchivesTask); } }); } private void _configureTasksExecuteGulpLocalDependencies( Project project, final Copy expandFrontendCSSCommonTask, final Project frontendThemeStyledProject, final Project frontendThemeUnstyledProject) { TaskContainer taskContainer = project.getTasks(); taskContainer.withType( ExecuteGulpTask.class, new Action<ExecuteGulpTask>() { @Override public void execute(ExecuteGulpTask executeGulpTask) { _configureTaskExecuteGulpLocalDependencies( executeGulpTask, expandFrontendCSSCommonTask, frontendThemeStyledProject, frontendThemeUnstyledProject); } }); } private void _configureTaskUploadArchives( final Project project, PublishNodeModuleTask publishNodeModuleTask, Task updateVersionTask) { Task uploadArchivesTask = GradleUtil.getTask( project, BasePlugin.UPLOAD_ARCHIVES_TASK_NAME); if (FileUtil.exists(project, ".lfrbuild-missing-resources-importer")) { Action<Task> action = new Action<Task>() { @Override public void execute(Task task) { throw new GradleException( "Unable to publish " + project + ", resources-importer directory is missing"); } }; if (publishNodeModuleTask != null) { publishNodeModuleTask.doFirst(action); } uploadArchivesTask.doFirst(action); } if (!GradleUtil.isSnapshot(project)) { if (publishNodeModuleTask != null) { uploadArchivesTask.dependsOn(publishNodeModuleTask); } uploadArchivesTask.finalizedBy(updateVersionTask); } } private boolean _getPluginPackageProperty(Project project, String key) { File file = project.file( "src/WEB-INF/liferay-plugin-package.properties"); if (!file.exists()) { return false; } Properties properties = GUtil.loadProperties(file); return Boolean.parseBoolean(properties.getProperty(key)); } private Project _getThemeProject(Project project, String name) { Project parentProject = project.getParent(); Project themeProject = parentProject.findProject(name); if (themeProject == null) { themeProject = GradleUtil.getProject( project.getRootProject(), name); } return themeProject; } private static final String _FRONTEND_COMMON_CSS_NAME = "com.liferay.frontend.css.common"; private static final String _GROUP = "com.liferay.plugins"; private static final String _JSON_VERSION_REGEX = "\\n\\t\"version\": \"(.+)\""; }