buildframework/helium/sf/java/imaker/src/com/nokia/helium/imaker/ant/types/Configuration.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.types;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.DataType;
import org.apache.tools.ant.types.PatternSet;
import com.nokia.helium.imaker.IMaker;
import com.nokia.helium.imaker.IMakerException;
import com.nokia.helium.imaker.ant.Command;
import com.nokia.helium.imaker.ant.IMakerCommandSet;
/**
* The imakerconfiguration enables the build manager to configure his iMaker
* builds based on introspection.
*
* The makefileset element will configure the filtering of the "imaker help-config"
* command. Then for each of the configuration found the targetset elements will be used
* to filter the output from the "imaker -f <i>configuration.mk</i> help-target-*-list"
* command. Finally a set of command will be generated.
*
* Each command will then be configure using the set of variables defined by the variableset
* elements. Only the WORKDIR variable is under the task control to ensure call safety during the
* parallelization.
*
* The usage of the variablegroup will allow you to duplicate the common set of commands
* and apply additional variables.
*
* Example:
* <pre>
* <imakerconfiguration regionalVariation="true">
* <makefileset>
* <include name="**/product/*ui.mk"/>
* </makefileset>
* <targetset>
* <include name="^core$" />
* <include name="langpack_\d+" />
* <include name="^custvariant_.*$" />
* <include name="^udaerase$" />
* </targetset>
* <variableset>
* <variable name="USE_FOTI" value="0"/>
* <variable name="USE_FOTA" value="1"/>
* </variableset>
* <variablegroup>
* <variable name="TYPE" value="rnd"/>
* </variablegroup>
* <variablegroup>
* <variable name="TYPE" value="subcon"/>
* </variablegroup>
* </imakerconfiguration>
* </pre>
*
* This configuration might produce the following calls :
* <pre>
* imaker -f /epoc32/rom/config/platform/product/image_conf_product_ui.mk TYPE=rnd USE_FOTI=0 USE_FOTA=1 core
* imaker -f /epoc32/rom/config/platform/product/image_conf_product_ui.mk TYPE=subcon USE_FOTI=0 USE_FOTA=1 core
* imaker -f /epoc32/rom/config/platform/product/image_conf_product_ui.mk TYPE=rnd USE_FOTI=0 USE_FOTA=1 langpack_01
* imaker -f /epoc32/rom/config/platform/product/image_conf_product_ui.mk TYPE=subcon USE_FOTI=0 USE_FOTA=1 langpack_01
* </pre>
*
* Remember to update helium/sf/java/imaker/imaker.rst if you change this
*
* @ant.type name="imakerconfiguration" category="imaker"
*/
public class Configuration extends DataType implements IMakerCommandSet {
private Vector<PatternSet> makefiles = new Vector<PatternSet>();
private Vector<MakefileSelector> selectors = new Vector<MakefileSelector>();
private Vector<PatternSet> targets = new Vector<PatternSet>();
private Vector<VariableSet> variables = new Vector<VariableSet>();
private Vector<VariableGroup> variablegroups = new Vector<VariableGroup>();
private boolean regionalVariation;
/**
* Create a makefileset element.
* Makefileset elements are based on regular Ant PatternSet.
* @return a PatternSet object.
*/
public PatternSet createMakefileSet() {
PatternSet makefile = new PatternSet();
makefiles.add(makefile);
return makefile;
}
/**
* Get the list of makefileset element.
* @return a vector of PatternSet objects.
*/
public Vector<PatternSet> getMakefileSet() {
return makefiles;
}
/**
* Add a Makefile selector configuration (e.g: products)
* @param filter
*/
public void add(MakefileSelector filter) {
selectors.add(filter);
}
/**
* Create a targetset element.
* Targetset elements are based on regular Ant PatternSet.
* @return a PatternSet object.
*/
public PatternSet createTargetSet() {
PatternSet target = new PatternSet();
targets.add(target);
return target;
}
/**
* Get the list of targetset.
* @return a vector of PatternSet objects.
*/
public Vector<PatternSet> getTargetSet() {
return targets;
}
/**
* Create a VariableSet element.
* @return a VariableSet object.
*/
public VariableSet createVariableSet() {
VariableSet var = new VariableSet();
variables.add(var);
return var;
}
/**
* Create a VariableSet element.
* @return a VariableSet object.
*/
public VariableGroup createVariableGroup() {
VariableGroup var = new VariableGroup();
variablegroups.add(var);
return var;
}
/**
* Get the list of variableset.
* @return a vector of VariableSet objects.
*/
public Vector<VariableSet> getVariableSet() {
return variables;
}
/**
* Enables the sorting of images per region.
* @deprecated The usage of this feature is now ignored.
* @param value the state of the regional variation
* @ant.not-required Default is false - The usage of this feature is now ignored.
*/
@Deprecated
public void setRegionalVariation(boolean value) {
log(this.getDataTypeName() + ": the usage of the regionalVariation attribute is now ignored.", Project.MSG_WARN);
regionalVariation = value;
}
/**
* Get the status of the regional variation enabling.
* @deprecated The usage of this feature is now ignored.
* @return returns true is the regional variation should be enabled.
*/
public boolean getRegionalVariation() {
return regionalVariation;
}
/**
* Check if name is matching any of the pattern under patterns list.
* @param name the string to match
* @param patterns a list of PatternSet
* @return Returns true if name matches at least one pattern.
*/
protected boolean isIncluded(String name, Vector<PatternSet> patterns) {
for (PatternSet patternSet : patterns) {
if (patternSet.isReference()) {
patternSet = (PatternSet) patternSet.getRefid().getReferencedObject();
}
String[] includes = patternSet.getIncludePatterns(getProject());
if (includes != null) {
for (String pattern : includes) {
if (Pattern.matches(pattern, name)) {
return true;
}
}
}
}
return false;
}
/**
* Check if name is matching any of the pattern under patterns list.
* @param name the string to match
* @param patterns a list of PatternSet
* @return Returns true if name matches at least one pattern.
*/
protected boolean isExcluded(String name, Vector<PatternSet> patterns) {
for (PatternSet patternSet : patterns) {
if (patternSet.isReference()) {
patternSet = (PatternSet) patternSet.getRefid().getReferencedObject();
}
String[] excludes = patternSet.getExcludePatterns(getProject());
if (excludes != null) {
for (String pattern : excludes) {
if (Pattern.matches(pattern, name)) {
return true;
}
}
}
}
return false;
}
/**
* Get a configured matcher.
* @return a configured makefile matcher.
*/
protected Matcher getMakefileMatcher(IMaker imaker) {
Matcher matcher = new Matcher();
List<String> includes = new ArrayList<String>();
List<String> excludes = new ArrayList<String>();
for (PatternSet patternSet : makefiles) {
if (patternSet.isReference()) {
patternSet = (PatternSet) patternSet.getRefid().getReferencedObject();
}
String[] patterns = patternSet.getIncludePatterns(getProject());
if (patterns != null) {
for (String pattern : patterns) {
includes.add(pattern);
}
}
patterns = patternSet.getExcludePatterns(getProject());
if (patterns != null) {
for (String pattern : patterns) {
excludes.add(pattern);
}
}
}
matcher.setIncludes(includes.toArray(new String[includes.size()]));
matcher.setExcludes(excludes.toArray(new String[excludes.size()]));
matcher.setBasedir(imaker.getEpocroot());
return matcher;
}
/**
* {@inheritDoc}
*/
@Override
public List<List<Command>> getCommands(IMaker imaker) {
List<List<Command>> cmdSet = new ArrayList<List<Command>>();
List<Command> cmds = new ArrayList<Command>();
// Let's add one fake group.
if (variablegroups.size() == 0) {
variablegroups.add(new VariableGroup());
}
try {
for (String configuration : getConfigurations(imaker.getConfigurations(), imaker)) {
log("Including configuration: " + configuration);
for (String target : imaker.getTargets(configuration)) {
if (isIncluded(target, targets) && !isExcluded(target, targets)) {
log("Including target: " + target);
for (VariableGroup group : variablegroups) {
if (group.isReference()) {
group = (VariableGroup)group.getRefid().getReferencedObject();
}
Command cmd = new Command();
cmd.setCommand("imaker");
cmd.addArgument("-f");
cmd.addArgument(configuration);
// Adding variables
for (VariableSet vs : variables) {
cmd.addVariables(vs.toMap());
}
// Adding variables from groups
cmd.addVariables(group.toMap());
cmd.setTarget(target);
cmds.add(cmd);
}
}
}
}
} catch (IMakerException e) {
throw new BuildException(e.getMessage());
}
// adding all the commands.
if (cmds.size() > 0) {
cmdSet.add(cmds);
}
return cmdSet;
}
/**
* Select which iMaker configuration should be built.
* @param configurations
* @return
*/
protected Set<String> getConfigurations(List<String> configurations, IMaker imaker) {
Set<String> result = new HashSet<String>();
if (makefiles.size() > 0) {
Matcher matcher = getMakefileMatcher(imaker);
for (String configuration : configurations) {
if (matcher.match(configuration)) {
result.add(configuration);
}
}
}
for (MakefileSelector selector : selectors) {
result.addAll(selector.selectMakefile(configurations));
}
return result;
}
/**
* Matcher object to filter discovered configurations.
* iMaker configuration.
*/
public class Matcher extends DirectoryScanner {
/**
* Check is a particular configuration can
* is selected.
* @param path the string to match.
* @return return true is the path is selected.
*/
public boolean match(String path) {
String vpath = path.replace('/', File.separatorChar).
replace('\\', File.separatorChar);
return isIncluded(vpath) && !isExcluded(vpath);
}
}
}