package org.gradle.samples.plugins; import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Transformer; import org.gradle.api.artifacts.ConfigurablePublishArtifact; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.ModuleVersionIdentifier; import org.gradle.api.attributes.Usage; import org.gradle.api.component.PublishableComponent; import org.gradle.api.component.SoftwareComponentContainer; import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier; import org.gradle.api.internal.component.SoftwareComponentInternal; import org.gradle.api.internal.component.UsageContext; import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.AppliedPlugin; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskProvider; import org.gradle.language.cpp.CppApplication; import org.gradle.language.cpp.CppBinary; import org.gradle.language.cpp.CppLibrary; import org.gradle.language.cpp.internal.DefaultUsageContext; import org.gradle.language.cpp.internal.MainLibraryVariant; import org.gradle.language.cpp.tasks.CppCompile; import org.gradle.language.nativeplatform.internal.PublicationAwareComponent; import org.gradle.samples.tasks.GeneratePublicMacrosManifest; import java.io.File; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; public class CppPublicMacrosPlugin implements Plugin<Project> { @Override public void apply(Project project) { project.getPluginManager().withPlugin("cpp-library", new Action<AppliedPlugin>() { @Override public void execute(AppliedPlugin appliedPlugin) { Configuration cppPublicMacrosElements = createElementsConfiguration(project.getConfigurations(), project.getObjects()); Configuration cppPublicMacros = createConfiguration(project.getConfigurations(), project.getObjects()); TaskProvider<GeneratePublicMacrosManifest> generateTask = createTask(project.getTasks()); CppLibrary library = project.getExtensions().getByType(CppLibrary.class); configureLibrary(library, generateTask, project.getProviders(), cppPublicMacros); configureElementsConfiguration(cppPublicMacrosElements, generateTask); configurePublishing(project.getComponents(), project, cppPublicMacrosElements); } }); project.getPluginManager().withPlugin("cpp-application", new Action<AppliedPlugin>() { @Override public void execute(AppliedPlugin appliedPlugin) { Configuration cppPublicMacros = createConfiguration(project.getConfigurations(), project.getObjects()); CppApplication application = project.getExtensions().getByType(CppApplication.class); configureApplication(application, project.getProviders(), cppPublicMacros); } }); } private static void configurePublishing(SoftwareComponentContainer components, Project project, Configuration cppPublicMacrosElements) { components.withType(PublicationAwareComponent.class, new Action<PublicationAwareComponent>() { @Override public void execute(PublicationAwareComponent component) { MainLibraryVariant mainVariant = (MainLibraryVariant) component.getMainPublication(); mainVariant.addVariant(new PublicMacrosVariantComponent(project.getObjects(), project.getGroup().toString(), project.getName() + "_publicMacros", project.getVersion().toString(), cppPublicMacrosElements)); } }); } private static class PublicMacrosVariantComponent implements SoftwareComponentInternal, PublishableComponent { private final ObjectFactory objectFactory; private final String group; private final String name; private final String version; private final Configuration cppPublicMacrosElements; private PublicMacrosVariantComponent(ObjectFactory objectFactory, String group, String name, String version, Configuration cppPublicMacrosElements) { this.objectFactory = objectFactory; this.group = group; this.name = name; this.version = version; this.cppPublicMacrosElements = cppPublicMacrosElements; } @Override public ModuleVersionIdentifier getCoordinates() { return DefaultModuleVersionIdentifier.newId(group, name, version); } @Override public Set<? extends UsageContext> getUsages() { return Collections.singleton(new DefaultUsageContext(new DefaultUsageContext("cpp-public-macros", cppPublicMacrosElements.getAttributes()), cppPublicMacrosElements.getAllArtifacts(), cppPublicMacrosElements)); } @Override public String getName() { return "cppPublicMacros"; } } private static void configureApplication(CppApplication application, ProviderFactory providerFactory, Configuration cppPublicMacros) { application.getBinaries().whenElementFinalized(new Action<CppBinary>() { @Override public void execute(CppBinary binary) { configureCompileTaskWithDependencies(binary.getCompileTask().get(), providerFactory, cppPublicMacros); } }); } private static void configureElementsConfiguration(Configuration elements, TaskProvider<GeneratePublicMacrosManifest> generateTask) { elements.getOutgoing().artifact(generateTask.map(new Transformer<File, GeneratePublicMacrosManifest>() { @Override public File transform(GeneratePublicMacrosManifest it) { return it.getOutputFile().getAsFile().get(); } }), new Action<ConfigurablePublishArtifact>() { @Override public void execute(ConfigurablePublishArtifact it) { it.builtBy(generateTask); } }); } private static Configuration createConfiguration(ConfigurationContainer configurations, ObjectFactory objectFactory) { return configurations.create("cppPublicMacros", new Action<Configuration>() { @Override public void execute(Configuration configuration) { configurations.all(new Action<Configuration>() { @Override public void execute(Configuration it) { if (it.getName().toLowerCase().endsWith("implementation")) { configuration.extendsFrom(it); } } }); configuration.setCanBeConsumed(false); configuration.setCanBeResolved(true); configuration.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, "cpp-public-macros")); } }); } private static Configuration createElementsConfiguration(ConfigurationContainer configurations, ObjectFactory objectFactory) { return configurations.create("cppPublicMacrosElements", new Action<Configuration>() { @Override public void execute(Configuration configuration) { configurations.all(new Action<Configuration>() { @Override public void execute(Configuration it) { if (it.getName().toLowerCase().endsWith("implementation")) { configuration.extendsFrom(it); } } }); configuration.setCanBeConsumed(true); configuration.setCanBeResolved(false); configuration.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, "cpp-public-macros")); } }); } private static TaskProvider<GeneratePublicMacrosManifest> createTask(TaskContainer tasks) { return tasks.register("generatePublicMacros", GeneratePublicMacrosManifest.class, new Action<GeneratePublicMacrosManifest>() { @Override public void execute(GeneratePublicMacrosManifest task) { task.getOutputFile().set(new File(task.getTemporaryDir(), "public-macros.txt")); } }); } private static void configureLibrary(CppLibrary library, TaskProvider<GeneratePublicMacrosManifest> generateTask, ProviderFactory providerFactory, Configuration cppPublicMacros) { library.getBinaries().whenElementFinalized(new Action<CppBinary>() { @Override public void execute(CppBinary binary) { configureCompileTask(binary.getCompileTask().get(), generateTask, providerFactory, cppPublicMacros); } }); } private static void configureCompileTask(CppCompile compileTask, TaskProvider<GeneratePublicMacrosManifest> generateTask, ProviderFactory providerFactory, Configuration cppPublicMacros) { compileTask.dependsOn(generateTask); compileTask.getCompilerArgs().addAll(generateTask.map(new Transformer<Iterable<? extends String>, GeneratePublicMacrosManifest>() { @Override public Iterable<? extends String> transform(GeneratePublicMacrosManifest it) { List<String> result = new ArrayList<>(); for (GeneratePublicMacrosManifest.Macro macro : it.getMacros().get()) { result.add(macro.getAsFlag()); } return result; } })); configureCompileTaskWithDependencies(compileTask, providerFactory, cppPublicMacros); } private static void configureCompileTaskWithDependencies(CppCompile compileTask, ProviderFactory providerFactory, Configuration cppPublicMacros) { compileTask.dependsOn(cppPublicMacros); compileTask.getCompilerArgs().addAll(providerFactory.provider(new Callable<Iterable<String>>() { @Override public Iterable<String> call() throws Exception { List<String> result = new ArrayList<>(); for (File file : cppPublicMacros.resolve()) { for (String macro : Files.readAllLines(file.toPath())) { result.add("-D" + macro); } } return result; } })); } }