package com.prezi.spaghetti.haxe.gradle;
import com.google.common.util.concurrent.Callables;
import com.prezi.haxe.gradle.DefaultHaxeSourceSet;
import com.prezi.haxe.gradle.Har;
import com.prezi.haxe.gradle.HaxeBasePlugin;
import com.prezi.haxe.gradle.HaxeBinary;
import com.prezi.haxe.gradle.HaxeBinaryBase;
import com.prezi.haxe.gradle.HaxeCompile;
import com.prezi.haxe.gradle.HaxeExtension;
import com.prezi.haxe.gradle.HaxeTestBinary;
import com.prezi.haxe.gradle.incubating.FunctionalSourceSet;
import com.prezi.haxe.gradle.nodetest.HaxeNodeTestCompile;
import com.prezi.spaghetti.bundle.ModuleBundleFactory;
import com.prezi.spaghetti.gradle.PackageApplication;
import com.prezi.spaghetti.gradle.SpaghettiBasePlugin;
import com.prezi.spaghetti.gradle.SpaghettiPlugin;
import com.prezi.spaghetti.gradle.internal.SpaghettiExtension;
import com.prezi.spaghetti.gradle.internal.SpaghettiGeneratedSourceSet;
import com.prezi.spaghetti.gradle.internal.SpaghettiModule;
import com.prezi.spaghetti.gradle.internal.SpaghettiModuleData;
import com.prezi.spaghetti.gradle.internal.SpaghettiModuleFactory;
import com.prezi.spaghetti.gradle.internal.incubating.BinaryNamingScheme;
import com.prezi.spaghetti.haxe.gradle.internal.HaxeSpaghettiModule;
import com.prezi.spaghetti.packaging.ApplicationType;
import org.gradle.api.Action;
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.file.FileCollection;
import org.gradle.api.internal.ConventionTask;
import org.gradle.api.internal.file.FileResolver;
import org.gradle.internal.reflect.Instantiator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Callable;
/**
* Add Spaghetti support to Haxe.
*/
public class SpaghettiHaxePlugin implements Plugin<Project> {
private static final Logger logger = LoggerFactory.getLogger(SpaghettiHaxePlugin.class);
public static final String NODE_MUNIT_DEPENDENCIES = "nodeMunitDependencies";
private final Instantiator instantiator;
private final FileResolver fileResolver;
@Inject
public SpaghettiHaxePlugin(Instantiator instantiator, FileResolver fileResolver) {
this.instantiator = instantiator;
this.fileResolver = fileResolver;
}
@Override
public void apply(final Project project) {
// Spaghetti will be working with Haxe, might as well set it
project.getPlugins().apply(SpaghettiBasePlugin.class);
SpaghettiExtension spaghettiExtension = project.getExtensions().getByType(SpaghettiExtension.class);
spaghettiExtension.setLanguage("haxe");
project.getPlugins().apply(HaxeBasePlugin.class);
project.getPlugins().apply(SpaghettiPlugin.class);
final HaxeExtension haxeExtension = project.getExtensions().getByType(HaxeExtension.class);
// We'll be needing a "js" platform
haxeExtension.getTargetPlatforms().maybeCreate("js");
// Add Spaghetti generated sources to compile and source tasks
spaghettiExtension.getSources().getByName("main").withType(SpaghettiGeneratedSourceSet.class).all(new Action<SpaghettiGeneratedSourceSet>() {
@Override
public void execute(final SpaghettiGeneratedSourceSet spaghettiGeneratedSourceSet) {
addSpaghettiSourceSet(project, haxeExtension, spaghettiGeneratedSourceSet, HaxeBinary.class, "spaghetti");
}
});
// Add Spaghetti generated test sources to test compile and test source tasks
spaghettiExtension.getSources().getByName("test").withType(SpaghettiGeneratedSourceSet.class).all(new Action<SpaghettiGeneratedSourceSet>() {
@Override
public void execute(final SpaghettiGeneratedSourceSet spaghettiGeneratedSourceSet) {
addSpaghettiSourceSet(project, haxeExtension, spaghettiGeneratedSourceSet, HaxeTestBinary.class, "spaghetti");
}
});
// For every Haxe binary...
haxeExtension.getBinaries().withType(HaxeBinary.class).all(new Action<HaxeBinary>() {
@Override
public void execute(final HaxeBinary binary) {
// Add a compile, source and munit task
HaxeBasePlugin.createCompileTask(project, binary, HaxeCompile.class);
HaxeBasePlugin.createSourceTask(project, binary, Har.class);
registerSpaghettiModuleBinary(project, binary, Collections.singleton(binary), false);
}
});
haxeExtension.getBinaries().withType(HaxeTestBinary.class).all(new Action<HaxeTestBinary>() {
@Override
public void execute(final HaxeTestBinary testBinary) {
HaxeBasePlugin.createTestCompileTask(project, testBinary, testBinary.getCompileClass());
registerSpaghettiModuleBinary(project, testBinary, Collections.singleton(testBinary.getCompileTask()), true);
}
});
final Task npmTask = createSetupNodeDependenciesTask(project);
spaghettiExtension.getBinaries().withType(HaxeSpaghettiModule.class).all(new Action<HaxeSpaghettiModule>() {
@Override
public void execute(final HaxeSpaghettiModule moduleBinary) {
HaxeBinaryBase<?> binary = moduleBinary.getOriginal();
if (moduleBinary.isUsedForTesting() && binary instanceof HaxeTestBinary) {
HaxeTestBinary testBinary = (HaxeTestBinary) binary;
ConventionTask munitTask;
ApplicationType applicationType;
if (((HaxeTestBinary) binary).getCompileTask() instanceof HaxeNodeTestCompile) {
munitTask = HaxeBasePlugin.createMUnitTask(project, testBinary, MUnitNodeWithSpaghetti.class);
munitTask.dependsOn(npmTask);
applicationType = ApplicationType.COMMON_JS;
} else {
munitTask = HaxeBasePlugin.createMUnitTask(project, testBinary, MUnitWithSpaghetti.class);
applicationType = ApplicationType.AMD;
}
final PackageApplication appTask = createTestApplication(moduleBinary, testBinary, applicationType);
munitTask.getConventionMapping().map("testApplication", new Callable<File>() {
@Override
public File call() throws Exception {
return appTask.getOutputDirectory();
}
});
munitTask.getConventionMapping().map("testApplicationName", new Callable<String>() {
@Override
public String call() throws Exception {
return appTask.getApplicationName();
}
});
munitTask.dependsOn(appTask);
}
}
private PackageApplication createTestApplication(final HaxeSpaghettiModule moduleBinary, final HaxeTestBinary testBinary, ApplicationType applicationType) {
String packageTaskName = testBinary.getNamingScheme().getTaskName("package");
PackageApplication appBundleTask = project.getTasks().create(packageTaskName, PackageApplication.class);
appBundleTask.setDescription("Creates a testable application of " + testBinary);
appBundleTask.setGroup("test");
appBundleTask.getConventionMapping().map("outputDirectory", new Callable<File>() {
@Override
public File call() throws Exception {
return project.file(project.getBuildDir() + "/spaghetti/tests/" + testBinary.getName());
}
});
appBundleTask.getConventionMapping().map("dependentModules", new Callable<FileCollection>() {
@Override
public FileCollection call() throws Exception {
return project.files(moduleBinary.getBundleTask().getDependentModules(), moduleBinary.getBundleTask().getOutputDirectory());
}
});
appBundleTask.getConventionMapping().map("mainModule", new Callable<String>() {
@Override
public String call() throws Exception {
return ModuleBundleFactory.load(moduleBinary.getBundleTask().getOutputDirectory()).getName();
}
});
appBundleTask.getConventionMapping().map("applicationName", Callables.returning(testBinary.getName() + "_test.js"));
appBundleTask.getConventionMapping().map("type", Callables.returning(applicationType));
appBundleTask.getConventionMapping().map("execute", Callables.returning(false));
appBundleTask.dependsOn(moduleBinary.getBundleTask());
return appBundleTask;
}
});
}
private Task createSetupNodeDependenciesTask(final Project project) {
return project.getTasks().create(NODE_MUNIT_DEPENDENCIES, Task.class);
}
private <T extends HaxeBinaryBase<?>> void addSpaghettiSourceSet(final Project project, HaxeExtension haxeExtension, final SpaghettiGeneratedSourceSet spaghettiSourceSet, Class<T> binaryType, String sourceSetName) {
logger.debug("Adding {} to binaries in {}", spaghettiSourceSet, project.getPath());
Configuration config = project.getConfigurations().maybeCreate("spaghetti");
FunctionalSourceSet spaghetti = haxeExtension.getSources().maybeCreate(sourceSetName);
final DefaultHaxeSourceSet haxeSourceSet = instantiator.newInstance(DefaultHaxeSourceSet.class, spaghettiSourceSet.getName(), spaghetti, config, fileResolver);
haxeSourceSet.getSource().source(spaghettiSourceSet.getSource());
haxeSourceSet.builtBy(spaghettiSourceSet);
haxeExtension.getBinaries().withType(binaryType).all(new Action<T>() {
@Override
public void execute(T compiledBinary) {
compiledBinary.getSource().add(haxeSourceSet);
logger.debug("Added {} to {} in {}", spaghettiSourceSet, compiledBinary, project.getPath());
}
});
}
private void registerSpaghettiModuleBinary(Project project, final HaxeBinaryBase<?> binary, Collection<?> dependencies, final boolean testing) {
Callable<File> javaScriptFile = new Callable<File>() {
@Override
public File call() throws Exception {
return binary.getCompileTask().getOutputFile();
}
};
SpaghettiPlugin.registerSpaghettiModuleBinary(project, binary.getName(), javaScriptFile, null, null, dependencies, binary, new SpaghettiModuleFactory<HaxeBinaryBase<?>>() {
@Override
public SpaghettiModule create(BinaryNamingScheme namingScheme, SpaghettiModuleData data, HaxeBinaryBase<?> original) {
return new HaxeSpaghettiModule(namingScheme, data, original, testing);
}
});
}
}