buildframework/helium/sf/java/imaker/src/com/nokia/helium/imaker/ant/engines/EmakeEngine.java
/*
* Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.helium.imaker.ant.engines;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.DataType;
import com.nokia.helium.core.plexus.AntStreamConsumer;
import com.nokia.helium.core.plexus.FileStreamConsumer;
import com.nokia.helium.imaker.IMakerException;
import com.nokia.helium.imaker.ant.Command;
import com.nokia.helium.imaker.ant.Engine;
import com.nokia.helium.imaker.ant.taskdefs.IMakerTask;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
*
* Simplest possible definition of the type, e.g:
*
* <pre>
* <emakeEngine id="imaker.ec" />
* </pre>
*
* Emake engine with some custom configuration.
*
* <pre>
* <emakeEngine id="imaker.ec" >
* <arg value="--emake-annofile=imaker.anno.xml" />
* </emakeEngine>
* </pre>
*
* @ant.type name=emakeEngine category="imaker"
*/
public class EmakeEngine extends DataType implements Engine {
private Logger log = Logger.getLogger(getClass());
private IMakerTask task;
private List<Arg> customArgs = new ArrayList<Arg>();
private File template;
/**
* Holder for emake custom args.
*/
public class Arg {
private String value;
/**
* Get the value of the argument.
*
* @return the argument
*/
public String getValue() {
return value;
}
/**
* Define the additional command line parameter you want to add to emake invocation.
*
* @param value the additional command line parameter
* @ant.required
*/
public void setValue(String value) {
this.value = value;
}
}
/**
* {@inheritDoc}
*/
public void build(List<List<Command>> cmdSet) throws IMakerException {
File makefile = null;
try {
// Writing the makefile.
makefile = writeMakefile(cmdSet);
// Running Emake
runEmake(makefile);
}
finally {
if (makefile != null) {
makefile.delete();
}
}
}
/**
* Returns the jar file name containing this class
*
* @return a File object or null if not found.
* @throws IMakerException
*/
protected File getJarFile() throws IMakerException {
URL url = this.getClass().getClassLoader().getResource(this.getClass().getName().replace('.', '/')
+ ".class");
if (url.getProtocol().equals("jar") && url.getPath().contains("!/")) {
String fileUrl = url.getPath().split("!/")[0];
try {
return new File(new URL(fileUrl).getPath());
}
catch (MalformedURLException e) {
throw new IMakerException("Error determining the jar file where "
+ this.getClass().getName() + " is located.", e);
}
}
return null;
}
/**
* Run emake using defined makefile.
*
* @param makefile the makefile to build
* @throws IMakerException
*/
private void runEmake(File makefile) throws IMakerException {
FileStreamConsumer output = null;
if (task.getOutput() != null) {
try {
output = new FileStreamConsumer(task.getOutput());
}
catch (FileNotFoundException e) {
throw new IMakerException("Error creating the stream recorder: " + e.getMessage(), e);
}
}
try {
Emake emake = new Emake();
emake.setWorkingDir(task.getEpocroot());
List<String> args = new ArrayList<String>();
for (Arg arg : customArgs) {
if (arg.getValue() != null) {
args.add(arg.getValue());
}
}
args.add("-f");
args.add(makefile.getAbsolutePath());
args.add("all");
if (task.isVerbose()) {
emake.addOutputLineHandler(new AntStreamConsumer(task));
}
emake.addErrorLineHandler(new AntStreamConsumer(task, Project.MSG_ERR));
if (output != null) {
emake.addOutputLineHandler(output);
emake.addErrorLineHandler(output);
}
emake.execute(args.toArray(new String[args.size()]));
}
catch (IMakerException e) {
throw new IMakerException("Error executing emake: " + e.getMessage(), e);
}
finally {
if (output != null) {
output.close();
}
}
}
/**
* Create the Makefile based on the cmdSet build sequence.
*
* @param cmdSet
* @return
* @throws IMakerException
* @throws IOException
*/
private File writeMakefile(List<List<Command>> cmdSet) throws IMakerException {
try {
Configuration cfg = new Configuration();
Template template = null;
if (this.template != null) {
if (!this.template.exists()) {
throw new IMakerException("Could not find template file: "
+ this.template.getAbsolutePath());
}
task.log("Loading template: " + this.template.getAbsolutePath());
cfg.setTemplateLoader(new FileTemplateLoader(this.template.getParentFile()));
template = cfg.getTemplate(this.template.getName());
}
else {
cfg.setTemplateLoader(new ClassTemplateLoader(this.getClass(), ""));
template = cfg.getTemplate("build_imaker_roms_signing.mk.ftl");
}
File makefile = File.createTempFile("helium-imaker", ".mk", task.getEpocroot());
makefile.deleteOnExit();
StringWriter out = new StringWriter();
Map<String, Object> data = new Hashtable<String, Object>();
data.put("cmdSets", cmdSet);
data.put("makefile", makefile.getAbsoluteFile());
data.put("java_home", System.getProperty("java.home"));
File jar = getJarFile();
if (jar != null) {
task.log("Using "
+ jar
+ " as the utility container, make sure the file is available under an emake root.");
data.put("java_utils_classpath", jar.getAbsolutePath());
}
template.process(data, out);
log.debug(out.getBuffer().toString());
OutputStreamWriter output = new OutputStreamWriter(new FileOutputStream(makefile));
output.append(out.getBuffer().toString());
output.close();
return makefile;
}
catch (IOException e) {
throw new IMakerException("Error generating the makefile: " + e.getMessage(), e);
}
catch (TemplateException e) {
throw new IMakerException("Error while rendering the makefile template: "
+ e.getMessage(), e);
}
}
/**
* Add custom parameters for the emake invocation.
*
* @return a new Arg object.
*/
public Arg createArg() {
Arg arg = new Arg();
customArgs.add(arg);
return arg;
}
/**
* {@inheritDoc}
*/
@Override
public void setTask(IMakerTask task) {
this.task = task;
}
/**
* Defines an alternate template to use to generate the build sequence for emake.
*
* @ant.not-required
*/
public void setTemplate(File template) {
this.template = template;
}
}