提交 7d926f72 编写于 作者: J Jesse Glick

Removing hudson.matrix classes and associated resources as a first pass.

(cherry picked from commit 4aad749c)

Conflicts:
	core/src/main/java/hudson/matrix/MatrixBuild.java
	core/src/main/java/hudson/matrix/MatrixConfiguration.java
	core/src/main/java/hudson/matrix/MatrixProject.java
	core/src/main/resources/hudson/matrix/MatrixProject/configure-entries.jelly
上级 889b46cc
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.RestrictedSince;
import hudson.Util;
import hudson.matrix.MatrixBuild.MatrixBuildExecution;
import hudson.model.AbstractDescribableImpl;
import jenkins.model.Jenkins;
import hudson.util.QuotedStringTokenizer;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Arrays;
import java.util.Map;
/**
* Configuration axis.
*
* <p>
* This class represents a single dimension of the configuration matrix.
* For example, the JAX-WS RI test configuration might include
* one axis "container={glassfish,tomcat,jetty}" and another axis
* "stax={sjsxp,woodstox}", and so on.
*
* @author Kohsuke Kawaguchi
*/
public class Axis extends AbstractDescribableImpl<Axis> implements Comparable<Axis>, Iterable<String>, ExtensionPoint {
/**
* Name of this axis.
* Used as a variable name.
*
* @deprecated as of 1.373
* Use {@link #getName()}
*/
public final String name;
/**
* Possible values for this axis.
*
* @deprecated as of 1.373
* Use {@link #getValues()}
*/
@Restricted(NoExternalUse.class)
@RestrictedSince("1.463")
public final List<String> values;
public Axis(String name, List<String> values) {
if (values == null || values.isEmpty()) {
values = Collections.emptyList();
}
this.name = name;
this.values = new ArrayList<String>(values);
}
public Axis(String name, String... values) {
this(name,Arrays.asList(values));
}
/**
* Used to build {@link Axis} from form.
*
* Axis with empty values need to be removed later.
*/
@DataBoundConstructor
public Axis(String name, String valueString) {
this.name = name;
this.values = new ArrayList<String>(Arrays.asList(Util.tokenize(valueString)));
}
/**
* Returns true if this axis is a system-reserved axis
* that <strike>has</strike> used to have af special treatment.
*
* @deprecated as of 1.373
* System vs user difference are generalized into extension point.
*/
public boolean isSystem() {
return false;
}
public Iterator<String> iterator() {
return getValues().iterator();
}
public int size() {
return getValues().size();
}
public String value(int index) {
return getValues().get(index);
}
/**
* The inverse of {@link #value(int)}.
*/
public int indexOf(String value) {
return values.indexOf(value);
}
/**
* Axis is fully ordered so that we can convert between a list of axis
* and a string unambiguously.
*/
public int compareTo(Axis that) {
return this.name.compareTo(that.name);
}
/**
* Name of this axis.
* Used as a variable name.
*/
public String getName() {
return name;
}
/**
* Possible values for this axis.
*/
public List<String> getValues() {
return Collections.unmodifiableList(values);
}
/**
* Called right at the beginning of {@link MatrixBuild} execution to allow {@link Axis} to update {@link #values}
* based on the current build.
*
* <p>
* Historically, axes values are considered static. They were assumed to reflect what the user has typed in,
* and their values are changed only when the project is reconfigured. So abstractions are built around this
* notion, and so for example {@link MatrixProject} has the current axes and their values, which it uses
* to render its UI.
*
* <p>
* So when the need was identified to change the values of axes per build, we decided that this be represented
* as a kind of project configuration update (where a project gets reconfigured every time a build runs), and
* this call back was added to allow {@link Axis} to update the next return value from the {@link #getValues()}
* (which is typically done by updating {@link #values}.)
*
* <p>
* While it is not strictly required, because of these historical reasons, UI will look better if
* Future calls to {@link Axis#getValues()} return the same values as what this method returns (until
* the next rebuild call).
*
* @param context
* The ongoing build. Never null.
* @return
* Never null. Returns the updated set of values.
* @since 1.471
*/
public List<String> rebuild(MatrixBuildExecution context) {
return getValues();
}
@Override
public AxisDescriptor getDescriptor() {
return (AxisDescriptor)super.getDescriptor();
}
@Override
public String toString() {
return new StringBuilder().append(name).append("={").append(Util.join(values,",")).append('}').toString();
}
/**
* Used for generating the config UI.
* If the axis is big and occupies a lot of space, use newline for separator
* to display multi-line text.
*/
public String getValueString() {
int len=0;
for (String value : values)
len += value.length();
char delim = len>30 ? '\n' : ' ';
// Build string connected with delimiter, quoting as needed
StringBuilder buf = new StringBuilder(len+values.size()*3);
for (String value : values)
buf.append(delim).append(QuotedStringTokenizer.quote(value,""));
return buf.substring(1);
}
/**
* Parses the submitted form (where possible values are
* presented as a list of checkboxes) and creates an axis
*/
public static Axis parsePrefixed(StaplerRequest req, String name) {
List<String> values = new ArrayList<String>();
String prefix = name+'.';
Enumeration e = req.getParameterNames();
while (e.hasMoreElements()) {
String paramName = (String) e.nextElement();
if(paramName.startsWith(prefix))
values.add(paramName.substring(prefix.length()));
}
if(values.isEmpty())
return null;
return new Axis(name,values);
}
/**
* Previously we used to persist {@link Axis}, but now those are divided into subtypes.
* So upon deserialization, resolve to the proper type.
*/
public Object readResolve() {
if (getClass()!=Axis.class) return this;
/*
This method is necessary only because earlier versions of Jenkins treated
axis names "label" and "jdk" differently,
plus Axis was a concrete class, and we need to be able to read that back.
So this measure is not needed for newly added Axes.
*/
if (getName().equals("jdk"))
return new JDKAxis(getValues());
if (getName().equals("label"))
return new LabelAxis(getName(),getValues());
return new TextAxis(getName(),getValues());
}
/**
* Returns all the registered {@link AxisDescriptor}s.
*/
public static DescriptorExtensionList<Axis,AxisDescriptor> all() {
return Jenkins.getInstance().<Axis,AxisDescriptor>getDescriptorList(Axis.class);
}
/**
* Converts the selected value (which is among {@link #values}) and adds that to the given map,
* which serves as the build variables.
*/
public void addBuildVariable(String value, Map<String,String> map) {
map.put(name,value);
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.Util;
import hudson.model.Descriptor;
import hudson.model.Failure;
import jenkins.model.Jenkins;
import hudson.util.FormValidation;
import org.kohsuke.stapler.QueryParameter;
/**
* {@link Descriptor} for {@link Axis}
*
* @author Kohsuke Kawaguchi
*/
public abstract class AxisDescriptor extends Descriptor<Axis> {
protected AxisDescriptor(Class<? extends Axis> clazz) {
super(clazz);
}
protected AxisDescriptor() {
}
/**
* Return false if the user shouldn't be able to create thie axis from the UI.
*/
public boolean isInstantiable() {
return true;
}
/**
* Makes sure that the given name is good as a axis name.
*/
public FormValidation doCheckName(@QueryParameter String value) {
if(Util.fixEmpty(value)==null)
return FormValidation.ok();
try {
Jenkins.checkGoodName(value);
return FormValidation.ok();
} catch (Failure e) {
return FormValidation.error(e.getMessage());
}
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.thoughtworks.xstream.XStream;
import hudson.Util;
import hudson.util.RobustCollectionConverter;
import javax.annotation.Nullable;
import java.util.*;
/**
* List of {@link Axis}.
*
* @author Kohsuke Kawaguchi
*/
public class AxisList extends ArrayList<Axis> {
public AxisList() {
}
public AxisList(Collection<? extends Axis> c) {
super(c);
}
public AxisList(Axis... c) {
this(Arrays.asList(c));
}
public Axis find(String name) {
for (Axis a : this) {
if(a.name.equals(name))
return a;
}
return null;
}
/**
* Creates a subset of the list that only contains the type assignable to the specified type.
*/
public AxisList subList(Class<? extends Axis> subType) {
return new AxisList(Util.filter(this,subType));
}
@Override
public boolean add(Axis axis) {
return axis!=null && super.add(axis);
}
/**
* List up all the possible combinations of this list.
*/
public Iterable<Combination> list() {
List<Set<String>> axesList = Lists.newArrayList();
for (Axis axis : this)
axesList.add(new LinkedHashSet<String>(axis.getValues()));
return Iterables.transform(Sets.cartesianProduct(axesList), new Function<List<String>, Combination>() {
public Combination apply(@Nullable List<String> strings) {
assert strings != null;
return new Combination(AxisList.this, (String[]) strings.toArray(new String[0]));
}
});
}
/**
* {@link com.thoughtworks.xstream.converters.Converter} implementation for XStream.
*/
public static final class ConverterImpl extends RobustCollectionConverter {
public ConverterImpl(XStream xs) {
super(xs);
}
@Override
public boolean canConvert(Class type) {
return type==AxisList.class;
}
@Override
protected Object createCollection(Class type) {
return new AxisList();
}
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import groovy.lang.Binding;
import hudson.Util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
/**
* A particular combination of {@link Axis} values.
*
* For example, when axes are "x={1,2},y={3,4}", then
* [x=1,y=3] is a combination (out of 4 possible combinations)
*
* @author Kohsuke Kawaguchi
*/
public final class Combination extends TreeMap<String,String> implements Comparable<Combination> {
public Combination(AxisList axisList, List<String> values) {
for(int i=0; i<axisList.size(); i++)
super.put(axisList.get(i).getName(),values.get(i));
}
public Combination(AxisList axisList,String... values) {
this(axisList,Arrays.asList(values));
}
public Combination(Map<String,String> keyValuePairs) {
for (Map.Entry<String, String> e : keyValuePairs.entrySet())
super.put(e.getKey(),e.getValue());
}
public String get(Axis a) {
return get(a.getName());
}
/**
* Obtains the continuous unique index number of this {@link Combination}
* in the given {@link AxisList}.
*/
public int toIndex(AxisList axis) {
int r = 0;
for (Axis a : axis) {
r *= a.size();
r += a.indexOf(get(a));
}
return r;
}
/**
* Evaluates the given Groovy expression with values bound from this combination.
*
* <p>
* For example, if this combination is a=X,b=Y, then expressions like <tt>a=="X"</tt> would evaluate to
* true.
*/
public boolean evalGroovyExpression(AxisList axes, String expression) {
return evalGroovyExpression(axes, expression, new Binding());
}
/**
* @see #evalGroovyExpression(AxisList, String)
* @since 1.515
* @deprecated as of 1.528
* Use {@link FilterScript#apply(hudson.matrix.MatrixBuild.MatrixBuildExecution, Combination)}
*/
public boolean evalGroovyExpression(AxisList axes, String expression, Binding binding) {
return FilterScript.parse(expression).apply(axes, this, binding);
}
public int compareTo(Combination that) {
int d = this.size()-that.size();
if(d!=0) return d;
Iterator<Map.Entry<String,String>> itr = this.entrySet().iterator();
Iterator<Map.Entry<String,String>> jtr = that.entrySet().iterator();
while(itr.hasNext()) {
Map.Entry<String,String> i = itr.next();
Map.Entry<String,String> j = jtr.next();
d = i.getKey().compareTo(j.getKey());
if(d!=0) return d;
d = i.getValue().compareTo(j.getValue());
if(d!=0) return d;
}
return 0;
}
/**
* Works like {@link #toString()} but only include the given axes.
*/
public String toString(Collection<Axis> subset) {
if(size()==1 && subset.size()==1)
return values().iterator().next();
StringBuilder buf = new StringBuilder();
for (Axis a : subset) {
if(buf.length()>0) buf.append(',');
buf.append(a.getName()).append('=').append(get(a));
}
if(buf.length()==0) buf.append("default"); // special case to avoid 0-length name.
return buf.toString();
}
/**
* Gets the values that correspond to the specified axes, in their order.
*/
public List<String> values(Collection<? extends Axis> axes) {
List<String> r = new ArrayList<String>(axes.size());
for (Axis a : axes)
r.add(get(a));
return r;
}
/**
* Converts to the ID string representation:
* <tt>axisName=value,axisName=value,...</tt>
*
* @param sep1
* The separator between multiple axes.
* @param sep2
* The separator between axis name and value.
*/
public String toString(char sep1, char sep2) {
StringBuilder buf = new StringBuilder();
for (Map.Entry<String,String> e : entrySet()) {
if(buf.length()>0) buf.append(sep1);
buf.append(e.getKey()).append(sep2).append(e.getValue());
}
if(buf.length()==0) buf.append("default"); // special case to avoid 0-length name.
return buf.toString();
}
@Override
public String toString() {
return toString(',','=');
}
/**
* Gets the 8 character-wide hash code for this combination
*/
public String digest() {
return Util.getDigestOf(toString());
}
/**
* Reverse operation of {@link #toString()}.
*/
public static Combination fromString(String id) {
if(id.equals("default"))
return new Combination(Collections.<String,String>emptyMap());
Map<String,String> m = new HashMap<String,String>();
StringTokenizer tokens = new StringTokenizer(id, ",");
while(tokens.hasMoreTokens()) {
String token = tokens.nextToken();
int idx = token.indexOf('=');
if(idx<0)
throw new IllegalArgumentException("Can't parse "+id);
m.put(token.substring(0,idx),token.substring(idx+1));
}
return new Combination(m);
}
/**
* Creates compact string representation suitable for display purpose.
*
* <p>
* The string is made compact by looking for {@link Axis} whose values
* are unique, and omit the axis name.
*/
public String toCompactString(AxisList axes) {
Set<String> nonUniqueAxes = new HashSet<String>();
Map<String,Axis> axisByValue = new HashMap<String,Axis>();
for (Axis a : axes) {
for (String v : a.getValues()) {
Axis old = axisByValue.put(v,a);
if(old!=null) {
// these two axes have colliding values
nonUniqueAxes.add(old.getName());
nonUniqueAxes.add(a.getName());
}
}
}
StringBuilder buf = new StringBuilder();
for (Map.Entry<String,String> e : entrySet()) {
if(buf.length()>0) buf.append(',');
if(nonUniqueAxes.contains(e.getKey()))
buf.append(e.getKey()).append('=');
buf.append(e.getValue());
}
if(buf.length()==0) buf.append("default"); // special case to avoid 0-length name.
return buf.toString();
}
// read-only
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends String, ? extends String> map) {
throw new UnsupportedOperationException();
}
@Override
public String put(String key, String value) {
throw new UnsupportedOperationException();
}
@Override
public String remove(Object key) {
throw new UnsupportedOperationException();
}
/**
* Duck-typing for boolean expressions.
*
* @see Combination#evalGroovyExpression(AxisList,String)
*/
public static final class BooleanCategory {
/**
* x -> y
*/
public static Boolean implies(Boolean lhs, Boolean rhs) {
return !lhs || rhs;
}
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.Extension;
/**
* {@link AxisDescriptor} for manually entered default axis.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class DefaultAxisDescriptor extends AxisDescriptor {
public DefaultAxisDescriptor() {
super(Axis.class);
}
@Override
public String getDisplayName() {
return "Axis";
}
@Override
public boolean isInstantiable() {
return false;
}
}
package hudson.matrix;
import groovy.lang.GroovyRuntimeException;
import hudson.AbortException;
import hudson.Extension;
import hudson.console.ModelHyperlinkNote;
import hudson.matrix.MatrixBuild.MatrixBuildExecution;
import hudson.matrix.listeners.MatrixBuildListener;
import hudson.model.BuildListener;
import hudson.model.Cause.UpstreamCause;
import hudson.model.Queue;
import hudson.model.ResourceController;
import hudson.model.Result;
import hudson.model.Run;
import org.kohsuke.stapler.DataBoundConstructor;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.TreeSet;
/**
* {@link MatrixExecutionStrategy} that captures historical behavior.
*
* <p>
* This class is somewhat complex because historically this wasn't an extension point and so
* people tried to put various logics that cover different use cases into one place.
* Going forward, people are encouraged to create subtypes to implement a custom logic that suits their needs.
*
* @author Kohsuke Kawaguchi
* @since 1.456
*/
public class DefaultMatrixExecutionStrategyImpl extends MatrixExecutionStrategy {
private volatile boolean runSequentially;
/**
* Filter to select a number of combinations to build first
*/
private volatile String touchStoneCombinationFilter;
/**
* Required result on the touchstone combinations, in order to
* continue with the rest
*/
private volatile Result touchStoneResultCondition;
private volatile MatrixConfigurationSorter sorter;
@DataBoundConstructor
public DefaultMatrixExecutionStrategyImpl(Boolean runSequentially, boolean hasTouchStoneCombinationFilter, String touchStoneCombinationFilter, Result touchStoneResultCondition, MatrixConfigurationSorter sorter) {
this(runSequentially!=null ? runSequentially : false,
hasTouchStoneCombinationFilter ? touchStoneCombinationFilter : null,
hasTouchStoneCombinationFilter ? touchStoneResultCondition : null,
sorter);
}
public DefaultMatrixExecutionStrategyImpl(boolean runSequentially, String touchStoneCombinationFilter, Result touchStoneResultCondition, MatrixConfigurationSorter sorter) {
this.runSequentially = runSequentially;
this.touchStoneCombinationFilter = touchStoneCombinationFilter;
this.touchStoneResultCondition = touchStoneResultCondition;
this.sorter = sorter;
}
public DefaultMatrixExecutionStrategyImpl() {
this(false,false,null,null,null);
}
public boolean getHasTouchStoneCombinationFilter() {
return touchStoneCombinationFilter!=null;
}
/**
* If true, {@link MatrixRun}s are run sequentially, instead of running in parallel.
*
* TODO: this should be subsumed by {@link ResourceController}.
*/
public boolean isRunSequentially() {
return runSequentially;
}
public void setRunSequentially(boolean runSequentially) {
this.runSequentially = runSequentially;
}
public String getTouchStoneCombinationFilter() {
return touchStoneCombinationFilter;
}
public void setTouchStoneCombinationFilter(String touchStoneCombinationFilter) {
this.touchStoneCombinationFilter = touchStoneCombinationFilter;
}
public Result getTouchStoneResultCondition() {
return touchStoneResultCondition;
}
public void setTouchStoneResultCondition(Result touchStoneResultCondition) {
this.touchStoneResultCondition = touchStoneResultCondition;
}
public MatrixConfigurationSorter getSorter() {
return sorter;
}
public void setSorter(MatrixConfigurationSorter sorter) {
this.sorter = sorter;
}
@Override
public Result run(MatrixBuildExecution execution) throws InterruptedException, IOException {
Collection<MatrixConfiguration> touchStoneConfigurations = new HashSet<MatrixConfiguration>();
Collection<MatrixConfiguration> delayedConfigurations = new HashSet<MatrixConfiguration>();
filterConfigurations(
execution,
touchStoneConfigurations,
delayedConfigurations
);
if (notifyStartBuild(execution.getAggregators())) return Result.FAILURE;
if (sorter != null) {
touchStoneConfigurations = createTreeSet(touchStoneConfigurations, sorter);
delayedConfigurations = createTreeSet(delayedConfigurations, sorter);
}
if(!runSequentially)
for(MatrixConfiguration c : touchStoneConfigurations)
scheduleConfigurationBuild(execution, c);
Result r = Result.SUCCESS;
for (MatrixConfiguration c : touchStoneConfigurations) {
if(runSequentially)
scheduleConfigurationBuild(execution, c);
MatrixRun run = waitForCompletion(execution, c);
notifyEndBuild(run,execution.getAggregators());
r = r.combine(getResult(run));
}
PrintStream logger = execution.getListener().getLogger();
if (touchStoneResultCondition != null && r.isWorseThan(touchStoneResultCondition)) {
logger.printf("Touchstone configurations resulted in %s, so aborting...%n", r);
return r;
}
if(!runSequentially)
for(MatrixConfiguration c : delayedConfigurations)
scheduleConfigurationBuild(execution, c);
for (MatrixConfiguration c : delayedConfigurations) {
if(runSequentially)
scheduleConfigurationBuild(execution, c);
MatrixRun run = waitForCompletion(execution, c);
notifyEndBuild(run,execution.getAggregators());
logger.println(Messages.MatrixBuild_Completed(ModelHyperlinkNote.encodeTo(c), getResult(run)));
r = r.combine(getResult(run));
}
return r;
}
private void filterConfigurations(
final MatrixBuildExecution execution,
final Collection<MatrixConfiguration> touchStoneConfigurations,
final Collection<MatrixConfiguration> delayedConfigurations
) throws AbortException {
final MatrixBuild build = execution.getBuild();
final FilterScript combinationFilter = FilterScript.parse(execution.getProject().getCombinationFilter(), FilterScript.ACCEPT_ALL);
final FilterScript touchStoneFilter = FilterScript.parse(getTouchStoneCombinationFilter(), FilterScript.REJECT_ALL);
try {
for (MatrixConfiguration c: execution.getActiveConfigurations()) {
if (!MatrixBuildListener.buildConfiguration(build, c)) continue; // skip rebuild
final Combination combination = c.getCombination();
if (touchStoneFilter != null && touchStoneFilter.apply(execution, combination)) {
touchStoneConfigurations.add(c);
} else if (combinationFilter.apply(execution, combination)) {
delayedConfigurations.add(c);
}
}
} catch (GroovyRuntimeException ex) {
PrintStream logger = execution.getListener().getLogger();
logger.println(ex.getMessage());
ex.printStackTrace(logger);
throw new AbortException("Failed executing combination filter");
}
}
private Result getResult(@Nullable MatrixRun run) {
// null indicates that the run was cancelled before it even gets going
return run!=null ? run.getResult() : Result.ABORTED;
}
private boolean notifyStartBuild(List<MatrixAggregator> aggregators) throws InterruptedException, IOException {
for (MatrixAggregator a : aggregators)
if(!a.startBuild())
return true;
return false;
}
private void notifyEndBuild(MatrixRun b, List<MatrixAggregator> aggregators) throws InterruptedException, IOException {
if (b==null) return; // can happen if the configuration run gets cancelled before it gets started.
for (MatrixAggregator a : aggregators)
if(!a.endRun(b))
throw new AbortException();
}
private <T> TreeSet<T> createTreeSet(Collection<T> items, Comparator<T> sorter) {
TreeSet<T> r = new TreeSet<T>(sorter);
r.addAll(items);
return r;
}
/** Function to start schedule a single configuration
*
* This function schedule a build of a configuration passing all of the Matrixchild actions
* that are present in the parent build.
*
* @param exec Matrix build that is the parent of the configuration
* @param c Configuration to schedule
*/
private void scheduleConfigurationBuild(MatrixBuildExecution exec, MatrixConfiguration c) {
MatrixBuild build = exec.getBuild();
exec.getListener().getLogger().println(Messages.MatrixBuild_Triggering(ModelHyperlinkNote.encodeTo(c)));
// filter the parent actions for those that can be passed to the individual jobs.
List<MatrixChildAction> childActions = build.getActions(MatrixChildAction.class);
c.scheduleBuild(childActions, new UpstreamCause((Run)build));
}
private MatrixRun waitForCompletion(MatrixBuildExecution exec, MatrixConfiguration c) throws InterruptedException, IOException {
BuildListener listener = exec.getListener();
String whyInQueue = "";
long startTime = System.currentTimeMillis();
// wait for the completion
int appearsCancelledCount = 0;
while(true) {
MatrixRun b = c.getBuildByNumber(exec.getBuild().getNumber());
// two ways to get beyond this. one is that the build starts and gets done,
// or the build gets cancelled before it even started.
if(b!=null && !b.isBuilding()) {
Result buildResult = b.getResult();
if(buildResult!=null)
return b;
}
Queue.Item qi = c.getQueueItem();
if(b==null && qi==null)
appearsCancelledCount++;
else
appearsCancelledCount = 0;
if(appearsCancelledCount>=5) {
// there's conceivably a race condition in computating b and qi, as their computation
// are not synchronized. There are indeed several reports of Hudson incorrectly assuming
// builds being cancelled. See
// http://www.nabble.com/Master-slave-problem-tt14710987.html and also
// http://www.nabble.com/Anyone-using-AccuRev-plugin--tt21634577.html#a21671389
// because of this, we really make sure that the build is cancelled by doing this 5
// times over 5 seconds
listener.getLogger().println(Messages.MatrixBuild_AppearsCancelled(ModelHyperlinkNote.encodeTo(c)));
return null;
}
if(qi!=null) {
// if the build seems to be stuck in the queue, display why
String why = qi.getWhy();
if(why != null && !why.equals(whyInQueue) && System.currentTimeMillis()-startTime>5000) {
listener.getLogger().print("Configuration " + ModelHyperlinkNote.encodeTo(c)+" is still in the queue: ");
qi.getCauseOfBlockage().print(listener); //this is still shown on the same line
whyInQueue = why;
}
}
Thread.sleep(1000);
}
}
@Extension
public static class DescriptorImpl extends MatrixExecutionStrategyDescriptor {
@Override
public String getDisplayName() {
return "Classic";
}
}
}
package hudson.matrix;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import hudson.Util;
import hudson.matrix.Combination.BooleanCategory;
import hudson.matrix.MatrixBuild.MatrixBuildExecution;
import hudson.model.ParameterValue;
import hudson.model.ParametersAction;
import java.util.Map;
import static java.lang.Boolean.*;
/**
* Groovy filter script that accepts or rejects matrix {@link Combination}.
*
* Instances of this class is thread unsafe.
*
* @author Kohsuke Kawaguchi
*/
class FilterScript {
private final Script script;
FilterScript(Script script) {
this.script = script;
}
/**
* @param context
* Variables the script will see.
*/
boolean evaluate(Binding context) {
script.setBinding(context);
return TRUE.equals(script.run());
}
/**
* Obtains a number N such that "N%M==0" would create
* a reasonable sparse matrix for integer M.
*
* <p>
* This is bit like {@link Combination#toIndex(AxisList)}, but instead
* of creating a continuous number (which often maps different
* values of the same axis to the same index in modulo N residue ring,
* we use a prime number P as the base. I think this guarantees the uniform
* distribution in any N smaller than 2P (but proof, anyone?)
*/
private long toModuloIndex(AxisList axis, Combination c) {
long r = 0;
for (Axis a : axis) {
r += a.indexOf(c.get(a));
r *= 31;
}
return r;
}
/**
* Applies the filter to the specified combination in the context of {@code context}.
*/
public boolean apply(MatrixBuildExecution context, Combination combination) {
return apply(context.getProject().getAxes(), combination, getConfiguredBinding(context));
}
/*package*/ boolean apply(AxisList axes, Combination c, Binding binding) {
for (Map.Entry<String, String> e : c.entrySet())
binding.setVariable(e.getKey(),e.getValue());
binding.setVariable("index",toModuloIndex(axes,c));
binding.setVariable("uniqueId", c.toIndex(axes));
return evaluate(binding);
}
private Binding getConfiguredBinding(final MatrixBuildExecution execution) {
final Binding binding = new Binding();
final ParametersAction parameters = execution.getBuild().getAction(ParametersAction.class);
if (parameters == null) return binding;
for (final ParameterValue pv: parameters) {
if (pv == null) continue;
final String name = pv.getName();
final String value = pv.createVariableResolver(null).resolve(name);
binding.setVariable(name, value);
}
return binding;
}
public static FilterScript parse(String expression) {
return parse(expression, ACCEPT_ALL);
}
/**
* @since 1.541
*/
public static FilterScript parse(String expression, FilterScript defaultScript) {
if (Util.fixEmptyAndTrim(expression)==null)
return defaultScript;
GroovyShell shell = new GroovyShell();
return new FilterScript(shell.parse("use("+BooleanCategory.class.getName().replace('$','.')+") {"+expression+"}"));
}
private static final Script EMPTY = new Script() {
@Override
public Object run() {
return true;
}
};
/**
* Constant that always applies to any combination.
* @since 1.541
*/
/*package*/ static final FilterScript ACCEPT_ALL = new FilterScript(EMPTY) {
@Override
public boolean apply(MatrixBuildExecution context, Combination combination) {
return true;
}
};
/**
* Constant that does not apply to any combination.
* @since 1.541
*/
/*package*/ static final FilterScript REJECT_ALL = new FilterScript(EMPTY) {
@Override
public boolean apply(MatrixBuildExecution context, Combination combination) {
return false;
}
};
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.Extension;
import jenkins.model.Jenkins;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.Arrays;
import java.util.List;
/**
* {@link Axis} that selects available JDKs.
*
* @author Kohsuke Kawaguchi
*/
public class JDKAxis extends Axis {
/**
* JDK axis was used to be stored as a plain "Axis" with the name "jdk",
* so it cannot be configured by any other name.
*/
public JDKAxis(List<String> values) {
super("jdk", values);
}
@DataBoundConstructor
public JDKAxis(String[] values) {
super("jdk", Arrays.asList(values));
}
@Override
public boolean isSystem() {
return true;
}
@Extension
public static class DescriptorImpl extends AxisDescriptor {
@Override
public String getDisplayName() {
return Messages.JDKAxis_DisplayName();
}
/**
* If there's no JDK configured, there's no point in this axis.
*/
@Override
public boolean isInstantiable() {
return !Jenkins.getInstance().getJDKs().isEmpty();
}
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.Extension;
import hudson.Functions;
import hudson.Util;
import jenkins.model.Jenkins;
import hudson.model.labels.LabelAtom;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.List;
/**
* {@link Axis} that selects label expressions.
*
* @author Kohsuke Kawaguchi
*/
public class LabelAxis extends Axis {
@DataBoundConstructor
public LabelAxis(String name, List<String> values) {
super(name, values);
}
@Override
public boolean isSystem() {
return true;
}
@Override
public String getValueString() {
return Util.join(getValues(),"/");
}
@Extension
public static class DescriptorImpl extends AxisDescriptor {
@Override
public String getDisplayName() {
return Messages.LabelAxis_DisplayName();
}
/**
* If there's no distributed build set up, it's pointless to provide this axis.
*/
@Override
public boolean isInstantiable() {
Jenkins h = Jenkins.getInstance();
return !h.getNodes().isEmpty() || !h.clouds.isEmpty();
}
private String jsstr(String body, Object... args) {
return '\"'+Functions.jsStringEscape(String.format(body,args))+'\"';
}
public String buildLabelCheckBox(LabelAtom la, LabelAxis instance) {
return jsstr("<input type='checkbox' name='values' json='%s' ",
Functions.htmlAttributeEscape(la.getName()))
+String.format("+has(%s)+",jsstr(la.getName()))
+jsstr("/><label class='attach-previous'>%s (%s)</label>",
la.getName(),la.getDescription());
// '${h.jsStringEscape('<input type="checkbox" name="values" json="'+h.htmlAttributeEscape(l.name)+'" ')}'+has("${h.jsStringEscape(l.name)}")+'${h.jsStringEscape('/><label class="attach-previous">'+l.name+' ('+l.description+')</label>')}'
}
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.Extension;
import hudson.Util;
import jenkins.model.Jenkins;
import java.util.LinkedList;
import java.util.List;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* {@link Axis} that selects label expressions.
*
* @since 1.403
*/
public class LabelExpAxis extends Axis {
@DataBoundConstructor
public LabelExpAxis(String name, String values) {
super(name, getExprValues(values));
}
public LabelExpAxis(String name, List<String> values) {
super(name, values);
}
@Override
public boolean isSystem() {
return true;
}
@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) Jenkins.getInstance().getDescriptorOrDie(getClass());
}
public String getValuesString(){
StringBuffer sb = new StringBuffer();
for(String item: this.getValues()){
sb.append(item);
sb.append("\n");
}
return sb.toString();
}
@Extension
public static class DescriptorImpl extends AxisDescriptor {
@Override
public String getDisplayName() {
return Messages.LabelExpAxis_DisplayName();
}
/**
* If there's no distributed build set up, it's pointless to provide this axis.
*/
@Override
public boolean isInstantiable() {
Jenkins h = Jenkins.getInstance();
return !h.getNodes().isEmpty() || !h.clouds.isEmpty();
}
}
public static List<String> getExprValues(String valuesString){
List<String> expressions = new LinkedList<String>();
String[] exprs = valuesString.split("\n");
for(String expr: exprs){
expressions.add(Util.fixEmptyAndTrim(expr));
}
return expressions;
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import java.util.List;
import java.util.ArrayList;
import java.util.AbstractList;
import java.util.Map;
import java.util.HashMap;
/**
* Used to assist thegeneration of config table.
*
* <p>
* {@link Axis Axes} are split into four groups.
* {@link #x Ones that are displayed as columns},
* {@link #y Ones that are displayed as rows},
* {@link #z Ones that are listed as bullet items inside table cell},
* and those which only have one value, and therefore doesn't show up
* in the table.
*
* <p>
* Because of object reuse inside {@link Layouter}, this class is not thread-safe.
*
* @author Kohsuke Kawaguchi
*/
public abstract class Layouter<T> {
public final List<Axis> x,y,z;
/**
* Axes that only have one value.
*/
private final List<Axis> trivial = new ArrayList<Axis>();
/**
* Number of data columns and rows.
*/
private int xSize, ySize, zSize;
public Layouter(List<Axis> x, List<Axis> y, List<Axis> z) {
this.x = x;
this.y = y;
this.z = z;
init();
}
/**
* Automatically split axes to x,y, and z.
*/
public Layouter(AxisList axisList) {
x = new ArrayList<Axis>();
y = new ArrayList<Axis>();
z = new ArrayList<Axis>();
List<Axis> nonTrivialAxes = new ArrayList<Axis>();
for (Axis a : axisList) {
if(a.size()>1)
nonTrivialAxes.add(a);
else
trivial.add(a);
}
switch(nonTrivialAxes.size()) {
case 0:
break;
case 1:
z.add(nonTrivialAxes.get(0));
break;
case 2:
// use the longer axis in Y
Axis a = nonTrivialAxes.get(0);
Axis b = nonTrivialAxes.get(1);
x.add(a.size() > b.size() ? b : a);
y.add(a.size() > b.size() ? a : b);
break;
default:
// for size > 3, use x and y, and try to pack y more
for( int i=0; i<nonTrivialAxes.size(); i++ )
(i%3==1?x:y).add(nonTrivialAxes.get(i));
}
init();
}
private void init() {
xSize = calc(x,-1);
ySize = calc(y,-1);
zSize = calc(z,-1);
}
/**
* Computes the width of n-th X-axis.
*/
public int width(int n) {
return calc(x,n);
}
/**
* Computes the repeat count of n-th X-axis.
*/
public int repeatX(int n) {
int w = 1;
for( n--; n>=0; n-- )
w *= x.get(n).size();
return w;
}
/**
* Computes the width of n-th Y-axis.
*/
public int height(int n) {
return calc(y,n);
}
private int calc(List<Axis> l, int n) {
int w = 1;
for( n++ ; n<l.size(); n++ )
w *= l.get(n).size();
return w;
}
/**
* Gets list of {@link Row}s to be displayed.
*
* The {@link Row} object is reused, so every value
* in collection returns the same object (but with different values.)
*/
public List<Row> getRows() {
return new AbstractList<Row>() {
final Row row = new Row();
public Row get(int index) {
row.index = index;
return row;
}
public int size() {
return ySize;
}
};
}
/**
* Represents a row, which is a collection of {@link Column}s.
*/
public final class Row extends AbstractList<Column> {
private int index;
final Column col = new Column();
@Override
public Column get(int index) {
col.xp = index;
col.yp = Row.this.index;
return col;
}
@Override
public int size() {
return xSize;
}
public String drawYHeader(int n) {
int base = calc(y,n);
if(index/base==(index-1)/base && index!=0) return null; // no need to draw a new value
Axis axis = y.get(n);
return axis.value((index/base)%axis.getValues().size());
}
}
protected abstract T getT(Combination c);
public final class Column extends AbstractList<T> {
/**
* Cell position.
*/
private int xp,yp;
private final Map<String,String> m = new HashMap<String,String>();
public T get(int zp) {
m.clear();
buildMap(xp,x);
buildMap(yp,y);
buildMap(zp,z);
for (Axis a : trivial) {
if (a.size() > 0) {
m.put(a.name, a.value(0));
}
}
return getT(new Combination(m));
}
private void buildMap(int p, List<Axis> axes) {
int n = p;
for( int i= axes.size()-1; i>=0; i-- ) {
Axis a = axes.get(i);
m.put(a.name, a.value(n%a.size()));
n /= a.size();
}
}
public int size() {
return zSize;
}
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.model.Job;
import hudson.tasks.LogRotator;
import java.io.IOException;
import java.util.logging.Logger;
/**
* {@link LogRotator} for {@link MatrixConfiguration},
* which discards the builds if and only if it's discarded
* in the parent.
*
* <p>
* Because of the serialization compatibility, we can't easily
* refactor {@link LogRotator} into a contract and an implementation.
*
* @author Kohsuke Kawaguchi
*/
final class LinkedLogRotator extends LogRotator {
LinkedLogRotator(int artifactDaysToKeep, int artifactNumToKeep) {
super(-1, -1, artifactDaysToKeep, artifactNumToKeep);
}
/**
* @deprecated since 1.369
* Use {@link #LinkedLogRotator(int, int)}
*/
LinkedLogRotator() {
super(-1, -1, -1, -1);
}
@Override
public void perform(Job _job) throws IOException, InterruptedException {
// Let superclass handle clearing artifacts, if configured:
super.perform(_job);
MatrixConfiguration job = (MatrixConfiguration) _job;
// copy it to the array because we'll be deleting builds as we go.
for( MatrixRun r : job.getBuilds().toArray(new MatrixRun[0]) ) {
if(job.getParent().getBuildByNumber(r.getNumber())==null) {
LOGGER.fine("Deleting "+r.getFullDisplayName());
r.delete();
}
}
if(!job.isActiveConfiguration() && job.getLastBuild()==null) {
LOGGER.fine("Deleting "+job.getFullDisplayName()+" because the configuration is inactive and there's no builds");
job.delete();
}
}
private static final Logger LOGGER = Logger.getLogger(LinkedLogRotator.class.getName());
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.model.JobProperty;
import hudson.tasks.BuildWrapper;
import hudson.tasks.Publisher;
import hudson.ExtensionPoint;
import hudson.Launcher;
import hudson.model.BuildListener;
/**
* {@link Publisher}, {@link JobProperty}, {@link BuildWrapper} can optionally implement this interface
* to perform result aggregation across {@link MatrixRun}.
*
* <p>
* This is useful for example to aggregate all the test results
* in {@link MatrixRun} into a single table/graph.
*
* @author Kohsuke Kawaguchi
* @since 1.115
*/
public interface MatrixAggregatable extends ExtensionPoint {
/**
* Creates a new instance of the aggregator.
*
* <p>
* This method is called during the build of
* {@link MatrixBuild} and the created aggregator
* will perform the aggregation.
*
* @param build
* The build for which the aggregation shall happen. Never null.
* @param launcher
* Can be used to launch processes during the build.
* @param listener
* Progress report and errors during the aggregation should
* be sent to this object. Never null.
*
* @return
* null if the implementation is not willing to contribute
* an aggregator.
*
* @see MatrixAggregator#build
* @see MatrixAggregator#listener
*/
MatrixAggregator createAggregator(MatrixBuild build, Launcher launcher, BuildListener listener);
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.ExtensionPoint;
import hudson.Launcher;
import hudson.model.BuildListener;
import hudson.tasks.BuildStep;
import hudson.tasks.Publisher;
import java.io.IOException;
/**
* Performs the aggregation of {@link MatrixRun} results
* into {@link MatrixBuild}.
*
* <p>
* {@link MatrixAggregator} is a transitive stateful mutable object.
* Unlike {@link Publisher}, it is not persisted. Instead, a fresh
* instance is created for each {@link MatrixBuild}, and various
* methods on this class are invoked in the event callback style
* as the build progresses.
*
* <p>
* The end result of the aggregation should be
* {@link MatrixBuild#addAction(Action) contributed as actions}.
*
* @author Kohsuke Kawaguchi
* @since 1.115
* @see MatrixAggregatable
*/
public abstract class MatrixAggregator implements ExtensionPoint {
/**
* The build in progress. Never null.
*/
protected final MatrixBuild build;
protected final Launcher launcher;
/**
* The listener to send the output to. Never null.
*/
protected final BuildListener listener;
protected MatrixAggregator(MatrixBuild build, Launcher launcher, BuildListener listener) {
this.build = build;
this.launcher = launcher;
this.listener = listener;
}
/**
* Called before the build starts.
*
* @return
* true if the build can continue, false if there was an error
* and the build needs to be aborted.
* @see BuildStep#prebuild(AbstractBuild,BuildListener)
*/
public boolean startBuild() throws InterruptedException, IOException {
return true;
}
/**
* Called whenever one run is completed.
*
* @param run
* The completed {@link MatrixRun} object. Always non-null.
* @return
* See {@link #startBuild()} for the return value semantics.
*/
public boolean endRun(MatrixRun run) throws InterruptedException, IOException {
return true;
}
/**
* Called after all the {@link MatrixRun}s have been completed
* to indicate that the build is about to finish.
*
* @return
* See {@link #startBuild()} for the return value semantics.
*/
public boolean endBuild() throws InterruptedException, IOException {
return true;
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Red Hat, Inc., Tom Huybrechts
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.AbortException;
import hudson.Functions;
import hudson.Util;
import hudson.console.ModelHyperlinkNote;
import hudson.matrix.MatrixConfiguration.ParentBuildAction;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Executor;
import hudson.model.Fingerprint;
import hudson.model.Queue;
import hudson.model.Queue.Item;
import hudson.model.Result;
import hudson.util.HttpResponses;
import jenkins.model.Jenkins;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.interceptor.RequirePOST;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletException;
/**
* Build of {@link MatrixProject}.
*
* @author Kohsuke Kawaguchi
*/
public class MatrixBuild extends AbstractBuild<MatrixProject,MatrixBuild> {
private AxisList axes;
/**
* If non-null, the {@link MatrixBuild} originates from the given build number.
*/
private Integer baseBuild;
public MatrixBuild(MatrixProject job) throws IOException {
super(job);
}
public MatrixBuild(MatrixProject job, Calendar timestamp) {
super(job, timestamp);
}
public MatrixBuild(MatrixProject project, File buildDir) throws IOException {
super(project, buildDir);
}
public Object readResolve() {
// MatrixBuild.axes added in 1.285; default to parent axes for old data
if (axes==null)
axes = getParent().getAxes();
return this;
}
/**
* Deletes the build and all matrix configurations in this build when the button is pressed.
*/
@RequirePOST
public void doDoDeleteAll( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
checkPermission(DELETE);
// We should not simply delete the build if it has been explicitly
// marked to be preserved, or if the build should not be deleted
// due to dependencies!
String why = getWhyKeepLog();
if (why!=null) {
sendError(hudson.model.Messages.Run_UnableToDelete(toString(),why),req,rsp);
return;
}
List<MatrixRun> runs = getExactRuns();
for(MatrixRun run : runs){
why = run.getWhyKeepLog();
if (why!=null) {
sendError(hudson.model.Messages.Run_UnableToDelete(toString(),why),req,rsp);
return;
}
run.delete();
}
delete();
rsp.sendRedirect2(req.getContextPath()+'/' + getParent().getUrl());
}
/**
* Used by view to render a ball for {@link MatrixRun}.
*/
public final class RunPtr {
public final Combination combination;
private RunPtr(Combination c) { this.combination=c; }
public MatrixRun getRun() {
return MatrixBuild.this.getRun(combination);
}
/**
* Return the URL to the run that this pointer references.
*
* In the typical case, this creates {@linkplain #getShortUrl() a very short relative url}.
* If the referenced run is a nearest previous build, this method returns a longer URL to that exact build.
* {@link MatrixRun} which belongs to a given build {@link MatrixBuild}.
* If there is no run which belongs to the build, return url of run, which belongs to the nearest previous build.
*/
public String getNearestRunUrl() {
MatrixRun r = getRun();
if (r==null) return null;
if (getNumber()==r.getNumber())
return getShortUrl()+'/';
else
return Stapler.getCurrentRequest().getContextPath()+'/'+r.getUrl();
}
public String getShortUrl() {
return Util.rawEncode(combination.toString());
}
public String getTooltip() {
MatrixRun r = getRun();
if(r!=null) return r.getIconColor().getDescription();
Queue.Item item = Jenkins.getInstance().getQueue().getItem(getParent().getItem(combination));
if(item!=null)
return item.getWhy();
return null; // fall back
}
}
public Layouter<RunPtr> getLayouter() {
// axes can be null if build page is access right when build starts
return axes == null ? null : new Layouter<RunPtr>(axes) {
protected RunPtr getT(Combination c) {
return new RunPtr(c);
}
};
}
/**
* Sets the base build from which this build is derived.
* @since 1.416
*/
public void setBaseBuild(MatrixBuild baseBuild) {
this.baseBuild = (baseBuild==null || baseBuild==getPreviousBuild()) ? null : baseBuild.getNumber();
}
/**
* Returns the base {@link MatrixBuild} that this build originates from.
* <p>
* If this build is a partial build, unexecuted {@link MatrixRun}s are delegated to this build number.
*/
public MatrixBuild getBaseBuild() {
return baseBuild==null ? getPreviousBuild() : getParent().getBuildByNumber(baseBuild);
}
/**
* Gets the {@link MatrixRun} in this build that corresponds
* to the given combination.
*/
public MatrixRun getRun(Combination c) {
MatrixConfiguration config = getParent().getItem(c);
if(config==null) return null;
return getRunForConfiguration(config);
}
/**
* Like {@link #getRun(Combination)}, but do not approximate the result by earlier execution
* of the given combination (which is done for partial rebuild of the matrix.)
*/
public MatrixRun getExactRun(Combination c) {
MatrixConfiguration config = getParent().getItem(c);
if(config==null) return null;
return config.getBuildByNumber(getNumber());
}
/**
* Returns all {@link MatrixRun}s for this {@link MatrixBuild}.
*/
@Exported
public List<MatrixRun> getRuns() {
List<MatrixRun> r = new ArrayList<MatrixRun>();
for(MatrixConfiguration c : getParent().getItems()) {
MatrixRun b = getRunForConfiguration(c);
if (b != null) r.add(b);
}
return r;
}
private MatrixRun getRunForConfiguration(MatrixConfiguration c) {
for (MatrixBuild b=this; b!=null; b=b.getBaseBuild()) {
MatrixRun r = c.getBuildByNumber(b.getNumber());
if (r!=null) return r;
}
return null;
}
/**
* Returns all {@link MatrixRun}s for exactly this {@link MatrixBuild}.
* <p>
* Unlike {@link #getRuns()}, this method excludes those runs
* that didn't run and got inherited.
* @since 1.413
*/
public List<MatrixRun> getExactRuns() {
List<MatrixRun> r = new ArrayList<MatrixRun>();
for(MatrixConfiguration c : getParent().getItems()) {
MatrixRun b = c.getBuildByNumber(getNumber());
if (b != null) r.add(b);
}
return r;
}
@Override
public String getWhyKeepLog() {
MatrixBuild b = getNextBuild();
if (isLinkedBy(b))
return Messages.MatrixBuild_depends_on_this(b.getDisplayName());
return super.getWhyKeepLog();
}
/**
* @return True if another {@link MatrixBuild} build (passed as a parameter) depends on this build.
* @since 1.481
*/
public boolean isLinkedBy(MatrixBuild b) {
if(null == b)
return false;
for(MatrixConfiguration c : b.getParent().getActiveConfigurations()) {
MatrixRun r = c.getNearestOldBuild(b.getNumber());
if (r != null && r.getNumber()==getNumber())
return true;
}
return false;
}
/**
* True if this build didn't do a full build and it is depending on the result of the previous build.
*/
public boolean isPartial() {
for(MatrixConfiguration c : getParent().getActiveConfigurations()) {
MatrixRun b = c.getNearestOldBuild(getNumber());
if (b != null && b.getNumber()!=getNumber())
return true;
}
return false;
}
@Override
public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) {
try {
MatrixRun item = getRun(Combination.fromString(token));
if(item!=null) {
if (item.getNumber()==this.getNumber())
return item;
else {
// redirect the user to the correct URL
String url = Functions.joinPath(item.getUrl(), req.getRestOfPath());
String qs = req.getQueryString();
if (qs!=null) url+='?'+qs;
throw HttpResponses.redirectViaContextPath(url);
}
}
} catch (IllegalArgumentException _) {
// failed to parse the token as Combination. Must be something else
}
return super.getDynamic(token,req,rsp);
}
@Override
public void run() {
execute(new MatrixBuildExecution());
}
@Override
public Fingerprint.RangeSet getDownstreamRelationship(AbstractProject that) {
Fingerprint.RangeSet rs = super.getDownstreamRelationship(that);
for(MatrixRun run : getRuns())
rs.add(run.getDownstreamRelationship(that));
return rs;
}
/**
* Object that lives from the start of {@link MatrixBuild} execution to its end.
*
* Used to keep track of things that are needed only during the build.
*/
public class MatrixBuildExecution extends AbstractBuildExecution {
private final List<MatrixAggregator> aggregators = new ArrayList<MatrixAggregator>();
private Set<MatrixConfiguration> activeConfigurations;
/**
* Snapshot of {@link MatrixProject#getActiveConfigurations()} to ensure
* that the build will use a consistent view of it.
*/
public Set<MatrixConfiguration> getActiveConfigurations() {
return activeConfigurations;
}
/**
* Aggregators attached to this build execution, that are notified
* of every start/end of {@link MatrixRun}.
*/
public List<MatrixAggregator> getAggregators() {
return aggregators;
}
protected Result doRun(BuildListener listener) throws Exception {
MatrixProject p = getProject();
PrintStream logger = listener.getLogger();
// give axes a chance to rebuild themselves
activeConfigurations = p.rebuildConfigurations(this);
// list up aggregators
listUpAggregators(p.getPublishers().values());
listUpAggregators(p.getProperties().values());
listUpAggregators(p.getBuildWrappers().values());
axes = p.getAxes();
try {
return p.getExecutionStrategy().run(this);
} catch( InterruptedException e ) {
logger.println("Aborted");
Executor x = Executor.currentExecutor();
x.recordCauseOfInterruption(MatrixBuild.this, listener);
return x.abortResult();
} catch (AbortException e) {
logger.println(e.getMessage());
return Result.FAILURE;
} finally {
// if the build was aborted in the middle. Cancel all the configuration builds.
Queue q = Jenkins.getInstance().getQueue();
synchronized(q) {// avoid micro-locking in q.cancel.
final int n = getNumber();
for (MatrixConfiguration c : activeConfigurations) {
for (Item i : q.getItems(c)) {
ParentBuildAction a = i.getAction(ParentBuildAction.class);
if (a!=null && a.parent==getBuild()) {
q.cancel(i);
logger.println(Messages.MatrixBuild_Cancelled(ModelHyperlinkNote.encodeTo(c)));
}
}
MatrixRun b = c.getBuildByNumber(n);
if(b!=null && b.isBuilding()) {// executor can spend some time in post production state, so only cancel in-progress builds.
Executor exe = b.getExecutor();
if(exe!=null) {
logger.println(Messages.MatrixBuild_Interrupting(ModelHyperlinkNote.encodeTo(b)));
exe.interrupt();
}
}
}
}
}
}
private void listUpAggregators(Collection<?> values) {
for (Object v : values) {
if (v instanceof MatrixAggregatable) {
MatrixAggregatable ma = (MatrixAggregatable) v;
MatrixAggregator a = ma.createAggregator(MatrixBuild.this, launcher, listener);
if(a!=null)
aggregators.add(a);
}
}
}
public void post2(BuildListener listener) throws Exception {
for (MatrixAggregator a : aggregators)
a.endBuild();
}
}
}
/*
* The MIT License
*
* Copyright (c) 2012, Chris Johnson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.model.Action;
import hudson.model.Queue;
import hudson.model.Queue.Task;
/**
* Optional interface for {@link Action}s that are used as parameters
* to {@link Queue#schedule(Task, int, Action...)} to indicate that they
* want to be also passed to the {@link MatrixRun}s from its parent {@link MatrixBuild}.
*
* @author Chris Johnson
* @since 1.481
*/
public interface MatrixChildAction extends Action {
}
/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.EnvVars;
import hudson.Util;
import hudson.model.Action;
import hudson.model.Executor;
import hudson.model.InvisibleAction;
import hudson.model.Node;
import hudson.model.Queue.QueueAction;
import hudson.model.TaskListener;
import hudson.util.AlternativeUiTextProvider;
import hudson.util.DescribableList;
import hudson.model.AbstractBuild;
import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.DependencyGraph;
import hudson.model.Descriptor;
import jenkins.model.BuildDiscarder;
import jenkins.model.Jenkins;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.JDK;
import hudson.model.Label;
import hudson.model.ParametersAction;
import hudson.model.Project;
import hudson.model.SCMedItem;
import hudson.model.Queue.NonBlockingTask;
import hudson.model.Cause.LegacyCodeCause;
import hudson.scm.SCM;
import jenkins.scm.SCMCheckoutStrategy;
import hudson.tasks.BuildWrapper;
import hudson.tasks.Builder;
import hudson.tasks.LogRotator;
import hudson.tasks.Publisher;
import java.io.IOException;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* One configuration of {@link MatrixProject}.
*
* @author Kohsuke Kawaguchi
*/
public class MatrixConfiguration extends Project<MatrixConfiguration,MatrixRun> implements SCMedItem, NonBlockingTask {
/**
* The actual value combination.
*/
private transient /*final*/ Combination combination;
/**
* Hash value of {@link #combination}. Cached for efficiency.
*/
private transient String digestName;
public MatrixConfiguration(MatrixProject parent, Combination c) {
super(parent,c.toString());
setCombination(c);
}
@Override
public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException {
// directory name is not a name for us --- it's taken from the combination name
super.onLoad(parent, combination.toString());
}
@Override
public EnvVars getEnvironment(Node node, TaskListener listener) throws IOException, InterruptedException {
EnvVars env = super.getEnvironment(node, listener);
AxisList axes = getParent().getAxes();
for (Map.Entry<String,String> e : getCombination().entrySet()) {
Axis a = axes.find(e.getKey());
if (a!=null)
a.addBuildVariable(e.getValue(),env); // TODO: hijacking addBuildVariable but perhaps we need addEnvVar?
else
env.put(e.getKey(), e.getValue());
}
return env;
}
@Override
protected void updateTransientActions(){
// This method is exactly the same as in {@link #AbstractProject}.
// Enabling to call this method from MatrixProject is the only reason for overriding.
super.updateTransientActions();
}
@Override
public boolean isConcurrentBuild() {
return getParent().isConcurrentBuild();
}
@Override
public void setConcurrentBuild(boolean b) throws IOException {
throw new UnsupportedOperationException("The setting can be only changed at MatrixProject");
}
/**
* Used during loading to set the combination back.
*/
/*package*/ void setCombination(Combination c) {
this.combination = c;
this.digestName = c.digest().substring(0,8);
}
/**
* Build numbers are always synchronized with the parent.
*
* <p>
* Computing this is bit tricky. Several considerations:
*
* <ol>
* <li>A new configuration build #N is started while the parent build #N is building,
* and when that happens we want to return N.
* <li>But the configuration build #N is done before the parent build #N finishes,
* and when that happens we want to return N+1 because that's going to be the next one.
* <li>Configuration builds might skip some numbers if the parent build is aborted
* before this configuration is built.
* <li>If nothing is building right now and the last build of the parent is #N,
* then we want to return N+1.
* </ol>
*/
@Override
public int getNextBuildNumber() {
AbstractBuild<?,?> lb = getParent().getLastBuild();
while (lb!=null && lb.isBuilding()) {
lb = lb.getPreviousBuild();
}
if(lb==null) return 0;
int n=lb.getNumber()+1;
lb = getLastBuild();
if(lb!=null)
n = Math.max(n,lb.getNumber()+1);
return n;
}
@Override
public int assignBuildNumber() throws IOException {
int nb = getNextBuildNumber();
MatrixRun r = getLastBuild();
if(r!=null && r.getNumber()>=nb) // make sure we don't schedule the same build twice
throw new IllegalStateException("Build #"+nb+" is already completed");
return nb;
}
@Override
public String getDisplayName() {
return combination.toCompactString(getParent().getAxes());
}
@Override
public MatrixProject getParent() {
return (MatrixProject)super.getParent();
}
/**
* Get the actual combination of the axes values for this {@link MatrixConfiguration}
*/
public Combination getCombination() {
return combination;
}
/**
* Since {@link MatrixConfiguration} is always invoked from {@link MatrixRun}
* once and just once, there's no point in having a quiet period.
*/
@Override
public int getQuietPeriod() {
return 0;
}
/**
* Inherit the value from the parent.
*/
@Override
public int getScmCheckoutRetryCount() {
return getParent().getScmCheckoutRetryCount();
}
/**
* Inherit the value from the parent.
*/
@Override
public SCMCheckoutStrategy getScmCheckoutStrategy() {
return getParent().getScmCheckoutStrategy();
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
protected Class<MatrixRun> getBuildClass() {
return MatrixRun.class;
}
@Override
protected MatrixRun newBuild() throws IOException {
List<Action> actions = Executor.currentExecutor().getCurrentWorkUnit().context.actions;
MatrixBuild lb = getParent().getLastBuild();
for (Action a : actions) {
if (a instanceof ParentBuildAction) {
MatrixBuild _lb = ((ParentBuildAction) a).parent;
if (_lb != null) {
lb = _lb;
}
}
}
if (lb == null) {
throw new IOException("cannot start a build of " + getFullName() + " since its parent has no builds at all");
}
// for every MatrixRun there should be a parent MatrixBuild
MatrixRun lastBuild = new MatrixRun(this, lb.getTimestamp());
lastBuild.number = lb.getNumber();
builds.put(lastBuild);
return lastBuild;
}
@Override
protected void buildDependencyGraph(DependencyGraph graph) {
}
@Override
public MatrixConfiguration asProject() {
return this;
}
@Override
public Label getAssignedLabel() {
// combine all the label axes by &&.
String expr;
String exprSlave = Util.join(combination.values(getParent().getAxes().subList(LabelAxis.class)), "&&");
String exprLabel = Util.join(combination.values(getParent().getAxes().subList(LabelExpAxis.class)), "&&");
if(!exprSlave.equals("") && !exprLabel.equals("")){
expr = exprSlave + "&&" + exprLabel;
} else{
expr = (exprSlave.equals("")) ? exprLabel : exprSlave;
}
return Jenkins.getInstance().getLabel(Util.fixEmpty(expr));
}
@Override
public String getPronoun() {
return AlternativeUiTextProvider.get(PRONOUN, this, Messages.MatrixConfiguration_Pronoun());
}
@Override
public JDK getJDK() {
return Jenkins.getInstance().getJDK(combination.get("jdk"));
}
//
// inherit build setting from the parent project
//
@Override
public List<Builder> getBuilders() {
return getParent().getBuilders();
}
@Override
public Map<Descriptor<Publisher>, Publisher> getPublishers() {
return getParent().getPublishers();
}
@Override
public DescribableList<Builder, Descriptor<Builder>> getBuildersList() {
return getParent().getBuildersList();
}
@Override
public DescribableList<Publisher, Descriptor<Publisher>> getPublishersList() {
return getParent().getPublishersList();
}
@Override
public Map<Descriptor<BuildWrapper>, BuildWrapper> getBuildWrappers() {
return getParent().getBuildWrappers();
}
@Override
public DescribableList<BuildWrapper, Descriptor<BuildWrapper>> getBuildWrappersList() {
return getParent().getBuildWrappersList();
}
@Override
public Publisher getPublisher(Descriptor<Publisher> descriptor) {
return getParent().getPublisher(descriptor);
}
@Override
public BuildDiscarder getBuildDiscarder() {
// TODO: LinkedLogRotator doesn't work very well in the face of pluggable BuildDiscarder but I don't know what to do
BuildDiscarder bd = getParent().getBuildDiscarder();
if (bd instanceof LogRotator) {
LogRotator lr = (LogRotator) bd;
return new LinkedLogRotator(lr.getArtifactDaysToKeep(),lr.getArtifactNumToKeep());
}
return new LinkedLogRotator();
}
@Override
public SCM getScm() {
return getParent().getScm();
}
/*package*/ String getDigestName() {
return digestName;
}
/**
* JDK cannot be set on {@link MatrixConfiguration} because
* it's controlled by {@link MatrixProject}.
* @deprecated
* Not supported.
*/
@Override
public void setJDK(JDK jdk) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @deprecated
* Value is controlled by {@link MatrixProject}.
*/
@Override
public void setBuildDiscarder(BuildDiscarder logRotator) {
throw new UnsupportedOperationException();
}
/**
* Returns true if this configuration is a configuration
* currently in use today (as opposed to the ones that are
* there only to keep the past record.)
*
* @see MatrixProject#getActiveConfigurations()
*/
public boolean isActiveConfiguration() {
return getParent().getActiveConfigurations().contains(this);
}
/**
* On Cygwin, path names cannot be longer than 256 chars.
* See http://cygwin.com/ml/cygwin/2005-04/msg00395.html and
* http://www.nabble.com/Windows-Filename-too-long-errors-t3161089.html for
* the background of this issue. Setting this flag to true would
* cause Jenkins to use cryptic but short path name, giving more room for
* jobs to use longer path names.
*/
public static boolean useShortWorkspaceName = Boolean.getBoolean(MatrixConfiguration.class.getName()+".useShortWorkspaceName");
/**
* @deprecated
* Use {@link #scheduleBuild(ParametersAction, Cause)}. Since 1.283
*/
public boolean scheduleBuild(ParametersAction parameters) {
return scheduleBuild(parameters, new LegacyCodeCause());
}
/** Starts the build with the ParametersAction that are passed in.
*
* @param parameters
* Can be null.
* @deprecated
* Use {@link #scheduleBuild(List, Cause)}. Since 1.480
*/
public boolean scheduleBuild(ParametersAction parameters, Cause c) {
return scheduleBuild(Collections.singletonList(parameters), c);
}
/**
* Starts the build with the actions that are passed in.
*
* @param actions Can be null.
* @param c Reason for starting the build
*/
public boolean scheduleBuild(List<? extends Action> actions, Cause c) {
List<Action> allActions = new ArrayList<Action>();
if(actions != null)
allActions.addAll(actions);
allActions.add(new ParentBuildAction());
allActions.add(new CauseAction(c));
return Jenkins.getInstance().getQueue().schedule2(this, getQuietPeriod(), allActions ).isAccepted();
}
/**
*
*/
public static class ParentBuildAction extends InvisibleAction implements QueueAction {
public transient MatrixBuild parent = (MatrixBuild)Executor.currentExecutor().getCurrentExecutable();
public boolean shouldSchedule(List<Action> actions) {
return true;
}
}
}
package hudson.matrix;
import hudson.ExtensionPoint;
import hudson.model.AbstractDescribableImpl;
import hudson.util.FormValidation;
import java.util.Comparator;
/**
* Add sorting for configurations {@link MatrixConfiguration}s of matrix job {@link MatrixProject}
*
* @since 1.439
* @author Lucie Votypkova
* @see MatrixConfigurationSorterDescriptor
*/
public abstract class MatrixConfigurationSorter extends AbstractDescribableImpl<MatrixConfigurationSorter> implements ExtensionPoint, Comparator<MatrixConfiguration> {
/**
* Checks if this sorter is properly configured and applicable for the given project.
*
* <p>
* This method is invoked when the configuration is submitted to ensure that the sorter is compatible
* with the current project configuration (most probably with its {@link Axis}.)
*
* @param p
* Project for which this sorter is being used for.
* @throws FormValidation
* If you need to report an error to the user and reject the form submission.
*/
public abstract void validate(MatrixProject p) throws FormValidation;
@Override
public MatrixConfigurationSorterDescriptor getDescriptor() {
return (MatrixConfigurationSorterDescriptor)super.getDescriptor();
}
}
package hudson.matrix;
import hudson.DescriptorExtensionList;
import hudson.model.Descriptor;
import jenkins.model.Jenkins;
/**
* Descriptor for {@link MatrixConfigurationSorter}.
*
* @author Kohsuke Kawaguchi
* @since 1.439
*/
public abstract class MatrixConfigurationSorterDescriptor extends Descriptor<MatrixConfigurationSorter> {
protected MatrixConfigurationSorterDescriptor(Class<? extends MatrixConfigurationSorter> clazz) {
super(clazz);
}
protected MatrixConfigurationSorterDescriptor() {
}
/**
* Returns all the registered {@link MatrixConfigurationSorterDescriptor}s.
*/
public static DescriptorExtensionList<MatrixConfigurationSorter,MatrixConfigurationSorterDescriptor> all() {
return Jenkins.getInstance().<MatrixConfigurationSorter,MatrixConfigurationSorterDescriptor>getDescriptorList(MatrixConfigurationSorter.class);
}
}
/*
* The MIT License
*
* Copyright (c) 2012, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.ExtensionPoint;
import hudson.matrix.MatrixBuild.MatrixBuildExecution;
import hudson.model.AbstractDescribableImpl;
import hudson.model.BuildListener;
import hudson.model.Result;
import java.io.IOException;
import java.util.List;
/**
* Controls the execution sequence of {@link MatrixConfiguration} when {@link MatrixProject} builds,
* including what degree it gets serialized/parallelled, how the whole build is abandoned when
* some fails, etc.
*
* @author Kohsuke Kawaguchi
* @since 1.456
*/
public abstract class MatrixExecutionStrategy extends AbstractDescribableImpl<MatrixExecutionStrategy> implements ExtensionPoint {
public Result run(MatrixBuildExecution execution) throws InterruptedException, IOException {
return run(execution.getBuild(), execution.getAggregators(), execution.getListener());
}
/**
* @deprecated
* Override {@link #run(MatrixBuild.MatrixBuildExecution)}
*/
public Result run(MatrixBuild build, List<MatrixAggregator> aggregators, BuildListener listener) throws InterruptedException, IOException {
throw new UnsupportedOperationException(getClass()+" needs to override run(MatrixBuildExecution)");
}
@Override
public MatrixExecutionStrategyDescriptor getDescriptor() {
return (MatrixExecutionStrategyDescriptor)super.getDescriptor();
}
}
/*
* The MIT License
*
* Copyright (c) 2012, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.DescriptorExtensionList;
import hudson.model.Descriptor;
import jenkins.model.Jenkins;
/**
* @author Kohsuke Kawaguchi
* @since 1.456
*/
public abstract class MatrixExecutionStrategyDescriptor extends Descriptor<MatrixExecutionStrategy> {
protected MatrixExecutionStrategyDescriptor(Class<? extends MatrixExecutionStrategy> clazz) {
super(clazz);
}
protected MatrixExecutionStrategyDescriptor() {
}
/**
* Returns all the registered {@link MatrixExecutionStrategyDescriptor}s.
*/
public static DescriptorExtensionList<MatrixExecutionStrategy,MatrixExecutionStrategyDescriptor> all() {
return Jenkins.getInstance().<MatrixExecutionStrategy,MatrixExecutionStrategyDescriptor>getDescriptorList(MatrixExecutionStrategy.class);
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.model.AbstractBuild;
import hudson.model.Build;
import hudson.model.Node;
import hudson.slaves.WorkspaceList;
import hudson.slaves.WorkspaceList.Lease;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
/**
* Execution of {@link MatrixConfiguration}.
*
* @author Kohsuke Kawaguchi
*/
public class MatrixRun extends Build<MatrixConfiguration,MatrixRun> {
public MatrixRun(MatrixConfiguration job) throws IOException {
super(job);
}
public MatrixRun(MatrixConfiguration job, Calendar timestamp) {
super(job, timestamp);
}
public MatrixRun(MatrixConfiguration project, File buildDir) throws IOException {
super(project, buildDir);
}
@Override
public String getUpUrl() {
StaplerRequest req = Stapler.getCurrentRequest();
if(req!=null) {
List<Ancestor> ancs = req.getAncestors();
for( int i=1; i<ancs.size(); i++) {
if(ancs.get(i).getObject()==this) {
Object parentObj = ancs.get(i-1).getObject();
if(parentObj instanceof MatrixBuild || parentObj instanceof MatrixConfiguration) {
return ancs.get(i-1).getUrl()+'/';
}
}
}
}
return super.getDisplayName();
}
/**
* Gets the {@link MatrixBuild} that has the same build number.
*
* @return
* null if no such build exists, which happens when the module build
* is manually triggered.
*/
public MatrixBuild getParentBuild() {
return getParent().getParent().getBuildByNumber(getNumber());
}
/**
* The same as {@link #getParentBuild()}.
*/
@Override
public AbstractBuild<?, ?> getRootBuild() {
return getParentBuild();
}
@Override
public String getDisplayName() {
StaplerRequest req = Stapler.getCurrentRequest();
if(req!=null) {
List<Ancestor> ancs = req.getAncestors();
for( int i=1; i<ancs.size(); i++) {
if(ancs.get(i).getObject()==this) {
if(ancs.get(i-1).getObject() instanceof MatrixBuild) {
return getParent().getCombination().toCompactString(getParent().getParent().getAxes());
}
}
}
}
return super.getDisplayName();
}
@Override
public Map<String,String> getBuildVariables() {
Map<String,String> r = super.getBuildVariables();
// pick up user axes
AxisList axes = getParent().getParent().getAxes();
for (Map.Entry<String,String> e : getParent().getCombination().entrySet()) {
Axis a = axes.find(e.getKey());
if (a!=null)
a.addBuildVariable(e.getValue(),r);
else
r.put(e.getKey(), e.getValue());
}
return r;
}
/**
* If the parent {@link MatrixBuild} is kept, keep this record too.
*/
@Override
public String getWhyKeepLog() {
MatrixBuild pb = getParentBuild();
if(pb!=null && pb.getWhyKeepLog()!=null)
return Messages.MatrixRun_KeptBecauseOfParent(pb);
return super.getWhyKeepLog();
}
@Override
public MatrixConfiguration getParent() {// don't know why, but javac wants this
return super.getParent();
}
@Override
public void run() {
execute(new MatrixRunExecution());
}
private class MatrixRunExecution extends BuildExecution {
protected Lease getParentWorkspaceLease(Node n, WorkspaceList wsl) throws InterruptedException, IOException {
MatrixProject mp = getParent().getParent();
String customWorkspace = mp.getCustomWorkspace();
if (customWorkspace != null) {
// we allow custom workspaces to be concurrently used between jobs.
return Lease.createDummyLease(n.getRootPath().child(getEnvironment(listener).expand(customWorkspace)));
}
return wsl.allocate(n.getWorkspaceFor(mp), getParentBuild());
}
@Override
protected Lease decideWorkspace(Node n, WorkspaceList wsl) throws InterruptedException, IOException {
MatrixProject mp = getParent().getParent();
// lock is done at the parent level, so that concurrent MatrixProjects get respective workspace,
// but within MatrixConfigurations that belong to the same MatrixBuild.
// if MatrixProject is configured with custom workspace, we assume that the user knows what he's doing
// and try not to append unique random suffix.
Lease baseLease = getParentWorkspaceLease(n,wsl);
// resolve the relative path against the parent workspace, which needs locking
FilePath baseDir = baseLease.path;
// prepare variables that can be used in the child workspace setting
EnvVars env = getEnvironment(listener);
env.put("COMBINATION",getParent().getCombination().toString('/','/')); // e.g., "axis1/a/axis2/b"
env.put("SHORT_COMBINATION",getParent().getDigestName()); // e.g., "0fbcab35"
env.put("PARENT_WORKSPACE",baseDir.getRemote());
env.putAll(getBuildVariables());
// child workspace need no individual locks, whether or not we use custom workspace
String childWs = mp.getChildCustomWorkspace();
return Lease.createLinkedDummyLease(baseDir.child(env.expand(childWs)),baseLease);
}
}
}
package hudson.matrix;
import hudson.Extension;
import hudson.util.FormValidation;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* Place holder for default "do not sort" {@link MatrixConfigurationSorter}.
*
* @author Kohsuke Kawaguchi
*/
public class NoopMatrixConfigurationSorter extends MatrixConfigurationSorter {
@DataBoundConstructor
public NoopMatrixConfigurationSorter() {
}
@Override
public void validate(MatrixProject p) throws FormValidation {
// nothing
}
public int compare(MatrixConfiguration o1, MatrixConfiguration o2) {
return o1.getDisplayName().compareTo(o2.getDisplayName());
}
@Extension(ordinal=100) // this is the default
public static class DescriptorImpl extends MatrixConfigurationSorterDescriptor {
@Override
public String getDisplayName() {
return "Doesn't care";
}
}
}
package hudson.matrix;
import hudson.Extension;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.List;
/**
* User-defined plain text axis.
*
* @author Kohsuke Kawaguchi
*/
public class TextAxis extends Axis {
public TextAxis(String name, List<String> values) {
super(name, values);
}
public TextAxis(String name, String... values) {
super(name, values);
}
@DataBoundConstructor
public TextAxis(String name, String valueString) {
super(name, valueString);
}
@Extension
public static class DescriptorImpl extends AxisDescriptor {
@Override
public String getDisplayName() {
return Messages.TextArea_DisplayName();
}
}
}
/*
* The MIT License
*
* Copyright (c) 2011, Christian Wolfgang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.matrix.listeners;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixRun;
import hudson.model.Action;
import jenkins.model.Jenkins;
import hudson.model.Queue;
import hudson.model.Queue.Task;
import java.util.List;
/**
* Controls which subset of {@link MatrixRun}s to rebuild.
*
* <p>
* Plugins can implement this extension point to filter out the subset of matrix project to build.
* Most typically, such a plugin would add a custom {@link Action} to a build when it goes to the queue
* ({@link Queue#schedule2(Task, int, List)}, then access this from {@link MatrixBuild} to drive
* the filtering logic.
*
* <p>
* See the matrix reloaded plugin for an example.
*
* @author Christian Wolfgang
* @since 1.413
*/
public abstract class MatrixBuildListener implements ExtensionPoint {
/**
* Determine whether to build a given configuration or not
*
* @param b
* Never null. The umbrella build.
* @param c
* The configuration whose build is being considered. If any of the {@link MatrixBuildListener}
* returns false, then the build for this configuration is skipped, and the previous build
* of this configuration will be taken as the default result.
* @return
* True to let the build happen, false to skip it.
*/
public abstract boolean doBuildConfiguration(MatrixBuild b, MatrixConfiguration c);
public static boolean buildConfiguration(MatrixBuild b, MatrixConfiguration c) {
for (MatrixBuildListener l : all()) {
if(!l.doBuildConfiguration(b, c)) {
return false;
}
}
return true;
}
/**
* Returns all the registered {@link MatrixBuildListener} descriptors.
*/
public static ExtensionList<MatrixBuildListener> all() {
return Jenkins.getInstance().getExtensionList(MatrixBuildListener.class);
}
}
<!--
The MIT License
Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<html><head/><body>
Matrix project
</body></html>
\ No newline at end of file
package hudson.matrix.DefaultMatrixExecutionStrategyImpl;
import hudson.matrix.MatrixConfigurationSorterDescriptor
import hudson.model.Result;
def f = namespace(lib.FormTagLib)
f.optionalBlock (field:"runSequentially", title:_("Run each configuration sequentially"), inline:true) {
if (MatrixConfigurationSorterDescriptor.all().size()>1) {
f.dropdownDescriptorSelector(title:_("Execution order of builds"), field:"sorter")
}
}
f.optionalBlock (field:"hasTouchStoneCombinationFilter", title:_("Execute touchstone builds first"), inline:true) {
// TODO: help="/help/matrix/touchstone.html">
// TODO: move l10n from MatrixProject/configEntries.jelly
f.entry(title:_("Filter"), field:"touchStoneCombinationFilter") {
f.textbox()
}
f.entry(title:_("Required result"), field:"touchStoneResultCondition", description:_("required.result.description")) {
select(name:"touchStoneResultCondition") {
f.option(value:"SUCCESS", selected:my.touchStoneResultCondition==Result.SUCCESS, _("Stable"))
f.option(value:"UNSTABLE", selected:my.touchStoneResultCondition==Result.UNSTABLE, _("Unstable"))
}
}
}
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc., Seiji Sogabe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
required.result.description=\
Execute the rest of the combinations only if the touchstone builds has (at least) the selected result.
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Execute\ touchstone\ builds\ first=K\u00f8r touchstone byg f\u00f8rst
Filter=Filter
Required\ result=N\u00f8dvendigt resultat
Run\ each\ configuration\ sequentially=K\u00f8r hver konfiguration sekventielt
Stable=Stabil
Unstable=Ustabil
required.result.description=Afvikl kun resten af kombinationerne hvis touchstone byggene har mindst det \u00f8nskede resultat.
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Required\ result=Mindest-Resultat
Stable=Stabil
Unstable=Instabil
required.result.description=F\u00fchre verbleibende Kombinationen nur aus, wenn \
die Touchstone-Builds mindestens das gew\u00e4hlte Resultat lieferten.
Execute\ touchstone\ builds\ first=Touchstone-Builds zuerst ausf\u00fchren
Run\ each\ configuration\ sequentially=Konfigurationen sequentiell ausf\u00fchren
Filter=Filter
# The MIT License
#
# Copyright 2012 sogabe.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Run\ each\ configuration\ sequentially=\u5404\u8a2d\u5b9a\u3092\u9806\u6b21\u8d77\u52d5
Execution\ order\ of\ builds=\u30d3\u30eb\u30c9\u306e\u5b9f\u884c\u9806\u5e8f
Execute\ touchstone\ builds\ first=\u6700\u521d\u306b\u7279\u5b9a\u306e\u30d3\u30eb\u30c9\u3092\u5b9f\u884c
Filter=\u30d5\u30a3\u30eb\u30bf\u30fc
Required\ result=\u30d3\u30eb\u30c9\u7d50\u679c
required.result.description=\
\u6700\u521d\u306b\u5b9f\u884c\u3057\u305f\u30d3\u30eb\u30c9\u304c(\u5c11\u306a\u304f\u3068\u3082)\u9078\u629e\u3057\u305f\u7d50\u679c\u3067\u3042\u308b\u5834\u5408\u306e\u307f\u3001\u6b8b\u308a\u306e\u7d44\u307f\u5408\u308f\u305b\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002
Stable=\u5b89\u5b9a
Unstable=\u4e0d\u5b89\u5b9a
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Reginaldo L. Russinholi, Cleiber Silva, Fernando Boaglio
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Execute\ touchstone\ builds\ first= Ordenar por builds
Unstable= Inst\u00e1vel
Run\ each\ configuration\ sequentially= Rodar cada configura\u00e7\u00e3o em sequ\u00eancia
Stable= Est\u00e1vel
Required\ result= Resultado
Filter= Filtro
required.result.description=Executar as demais combina\u00e7\u00f5es somente se os crit\u00e9rios de builds tiver (pelo menos) o resultado selecionado.
# The MIT License
#
# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Run\ each\ configuration\ sequentially=\u5faa\u5e8f\u57f7\u884c\u5404\u9805\u8a2d\u5b9a
Execution\ order\ of\ builds=\u5efa\u7f6e\u57f7\u884c\u9806\u5e8f
Execute\ touchstone\ builds\ first=\u512a\u5148\u5efa\u7f6e\u300c\u8a66\u91d1\u77f3\u300d
Filter=\u7be9\u9078\u689d\u4ef6
Required\ result=\u5fc5\u8981\u7d50\u679c
required.result.description=\
\u53ea\u6709\u5728\u300c\u8a66\u91d1\u77f3\u300d\u5efa\u7f6e\u7d50\u679c\u7b26\u5408\u9810\u671f\u6642\u624d\u57f7\u884c\u5176\u4ed6\u7d44\u5408\u3002
Stable=\u7a69\u5b9a
Unstable=\u4e0d\u7a69\u5b9a
\ No newline at end of file
<div>
Use a touchstone build if you want to run a sanity check before building all the combinations.
You can select a number of combinations using a combination filter. These will be built first.
If the result satisfies the result condition, the rest of the combinations will be built.
</div>
\ No newline at end of file
<div>
Ein "Touchstone"-Build ist ein besonderer Build, der Sie besonders frühzeitig auf Probleme
in Ihrem Code hinweist, z.B. durch Ausführung essentieller Plausibilitätstests.
<p>
Wenn angewählt, führt Jenkins zunächst die Touchstone-Builds aus, bevor dann alle weiteren
Kombinationen gebaut werden. Sie können über einen Kombinationsfilter auch mehrere Builds
als Touchstone-Build kennzeichnen. Nur wenn das Ergebnis der Touchstone-Builds die
Ergebnisbedingung erfüllt, werden alle weiteren Kombinationen gebaut.
</div>
\ No newline at end of file
<div>
すべての組み合わせをビルドする前に、正常かどうかチェックしたい場合に使用します。
フィルターを使用して、最初に実行するビルドの組み合わせを選択できます。
結果が"ビルド結果"の条件を満たした場合、残りの組み合わせがビルドされます。
</div>
\ No newline at end of file
<div>
如果您想在建置所有組合前先做些例行檢查,就可以使用「試金石」建置。
可以透過組合篩選條件選出一些組合,並優先建置它們。
只有在結果符合預期條件時,才會建置其他組合。
</div>
\ No newline at end of file
<div>
With this option checked, Jenkins builds each configuration in a sequence.
This can be useful if your configuration needs to access the shared resource
like database, as well as to avoid crowding out other jobs.
</div>
<div>
Wenn angewählt, baut Jenkins alle Konfigurationen sequentiell nacheinander.
Dies kann zum einen nützlich sein, wenn Konfigurationen eine gemeinsame Ressource
benötigen, z.B. eine Datenbank. Zum anderen vermeidet es die Verdrängung
anderer Jobs in der Build-Warteschlange.
</div>
<div>
各設定を順番にビルドします。これは、(複数ジョブが同時にビルドされることにより)他のジョブがビルドされないことを防ぐとともに、
データベースのような共有リソースへのアクセスを必要とする場合に役に立ちます。
</div>
\ No newline at end of file
<div>
選用本功能時,Jenkins 會依序建置各種不同的設定。
如果您的設定需要存取資料庫這類共用資料時就很好用,此外,也能避免排擠掉其他作業。
</div>
<!--
The MIT License
Copyright (c) 2010, InfraDNA, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!-- JDK config -->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<f:entry>
<j:forEach var="jdk" items="${app.JDKs}">
<f:checkbox name="values" json="${jdk.name}" checked="${instance.values.contains(jdk.name)}" title="${jdk.name}" />
<st:nbsp/>
</j:forEach>
</f:entry>
</j:jelly>
<!--
The MIT License
Copyright (c) 2010, InfraDNA, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<f:entry title="${%Name}" field="name">
<f:textbox default="label" />
</f:entry>
<f:entry title="${%Node/Label}" field="labels">
<div class="yahooTree labelAxis-tree" style="border: 1px solid gray; height: 10em; overflow:auto;" values="${instance.valueString}" />
<script>
Behaviour.specify("DIV.labelAxis-tree", 'LabelAxis', 0, function(e) {
var tree = new YAHOO.widget.TreeView(e);
var labels = new YAHOO.widget.TextNode("${%Labels}", tree.getRoot(), false);
var machines = new YAHOO.widget.TextNode("${%Individual nodes}", tree.getRoot(), false);
var values = (e.getAttribute("values") || "").split("/");
function has(v) {
return values.include(v) ? 'checked="checked" ' : "";
}
<j:forEach var="l" items="${app.labelAtoms}">
new YAHOO.widget.HTMLNode(<j:out value="${descriptor.buildLabelCheckBox(l,instance)}"/>, ${l.isSelfLabel()?'machines':'labels'}, false);
</j:forEach>
tree.draw();
<!--
force the rendering of HTML, so that input fields are there
even when the form is submitted without this tree expanded.
-->
tree.expandAll();
tree.collapseAll();
<!--
cancel the event.
from http://yuilibrary.com/forum/viewtopic.php?f=89&t=8209&p=26239&hilit=HTMLNode#p26239
"To prevent toggling and allow the link to work, add a listener to the clickEvent on that tree and simply return false"
-->
tree.subscribe("clickEvent", function(node) {
return false;
});
});
</script>
</f:entry>
</j:jelly>
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Node/Label=Node/Etiket
Labels=Etiketter
Individual\ nodes=Individuelle noder
Name=Navn
# The MIT License
#
# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Node/Label=Knoten/Label
Labels=Labels
Individual\ nodes=Einzelne Knoten
Name=Name
# The MIT License
#
# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Node/Label=Nodo/Etiqueta
Labels=Etiquetas
Individual\ nodes=Nodos individuales
Name=Nombre
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc., Seiji Sogabe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Name=\u540d\u524d
Node/Label=\u30ce\u30fc\u30c9/\u30e9\u30d9\u30eb
Labels=\u30e9\u30d9\u30eb
Individual\ nodes=\u500b\u5225\u306e\u30ce\u30fc\u30c9
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Node/Label=N\u00f3
Labels=Label
Individual\ nodes=N\u00f3s individuais
Name=Nome
# The MIT License
#
# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Name=\u540d\u7a31
Node/Label=\u7bc0\u9ede/\u6a19\u7c64
Labels=\u6a19\u7c64
Individual\ nodes=\u7368\u7acb\u7bc0\u9ede
<div>
Specify nodes where builds are performed.
<ul>
<li>
If none is selected, Jenkins will choose
an available node to perform a build (this is the same behavior as the free-style
project when "tie this project to node" is not selected). This is useful
when the project doesn't have a dependency on any particular node, as
it allows Jenkins to utilize nodes in an optimal fashion.
<li>
If one is selected, Jenkins will always run the build on the specified node
(if you select one from "individual nodes")
or one of the nodes that belongs to the label (if you select one from "labels").
This is useful when the build is required to run on a specific computer,
or a subset of computers. For example, maybe your build requires MacOS X.
<li>
If multiple values are selected, the configuration matrix will be expanded to include
all of them, and builds will be performed on all of the selected nodes/labels.
This is useful, for example, when you'd like to run tests on Windows, Linux,
and Solaris.
</ul>
<br>
During a build, the selected node name / label name for the given run is available as the "label" axis.
See the help of "axes" below for more information about how to access the axis value.
</div>
<div>
Geben Sie die Knoten an, auf denen Ihre Builds ausgeführt werden sollen.
<ul>
<li>
Ist kein Knoten ausgewählt, wird Jenkins einen verfügbaren Knoten auswählen,
um den Build durchzuführen (dies ist das gleiche Verhalten wie beim
"Free-Style-Projekt", wenn "Projekt an Knoten binden" abgewählt ist).
Dies ist sinnvoll, wenn das Projekt keine Abhängigkeiten zu einem bestimmten
Knoten aufweist, da es Jenkins erlaubt, die Knoten optimal einzusetzen.
<li>
Ist ein Knoten ausgewählt, wird Jenkins den Build immer auf dem gewählten Knoten
(wenn ein Knoten unter "Bestimmte Knoten" ausgewählt wurde) oder auf einem Knoten,
der zum gewählten Label gehört (wenn ein Knoten unter "Labels" ausgewählt wurde), ausführen.
Dies ist sinnvoll, wenn der Build auf einem bestimmten Rechner oder
Rechnergruppe ausgeführt werden soll. Beispielsweise könnte der Build ein
bestimmtes Betriebssystem wie MacOS X voraussetzen.
<li>
Sind mehrere Knoten ausgewählt, wird die Konfigurationsmatrix so erweitert,
dass sie alle Werte beinhaltet. Builds werden dann auf allen gewählten Knoten/Labels
ausgeführt.
Dies ist sinnvoll, wenn Sie beispielsweise Tests unter Windows, Linux und
Solaris ausführen möchten.
</ul>
<br>
Während eines Builds ist der aktuelle Name des Knotens bzw. des Labels als Achse
<tt>label</tt> sichtbar.
Im Hilfetext zu "Achsen" finden Sie weitere Informationen, wie Sie auf die
Werte der Achsenvariablen zugreifen können.
</div>
<div>
Spécifiez ici les machines (noeuds) sur lesquelles les builds sont
exécutés.
<ul>
<li>
Si aucune n'est sélectionnée, Jenkins choisira une des machines
disponibles pour l'exécution du build (ce comportement est le même
pour les projets free-style quand l'option "attacher ce projet au
noeud" n'est pas sélectionnée). C'est utile quand le projet n'a
pas de dépendance avec une machine particulière; Jenkins peut en effet
ainsi utiliser les noeuds de façon optimale.
<li>
Si une machine est sélectionnée, Jenkins lancera toujours le build
dessus (si vous en sélectionnez une parmi les "noeuds spécifiques")
ou sur une des machines qui appartiennent à un libellé (si vous
en choisissez une dans "libellé").
C'est pratique quand le build doit tourner sur un ordinateur
particulier ou sur un sous-ensemble d'ordinateurs. Par exemple,
si votre build nécessite MacOS X.
<li>
Si de multiples valeurs sont sélectionnées, la matrice de
configuration sera étendue de façon à les inclure toutes et les
builds seront exécutés sur tous les noeuds ou libellés.
Cela est utile, par exemple, quand vous souhaitez lancer des tests
sur Windows, Linux et Solaris.
</ul>
<br>
Au cours d'un build, le nom du noeud ou du libellé sélectionné pour un
lancement donné est visible sous l'axe "libellé".
Référez-vous à l'aide sur les "axes" ci-dessous pour plus d'informations
sur l'accès à la valeur de l'axe.
</div>
<div>
ビルドを実行するノードを選択します。
<ul>
<li>
ノードを選択すると、Jenkinsはビルドを実行可能なノードを選択します(この動作は、
"このビルドは指定のノード上でのみ実行"を選択していない場合のフリースタイルと同じです)。
プロジェクトが特定のノードに依存していないとき、Jenkinsはノードを最適な方法で利用できるので、
便利です。
<li>
1つだけ選択すると、Jenkinsはビルドを、指定されたノード("個別のノード"から1つ選択)か、
ラベルに属する複数ノードのうちの1つのノード("ラベル"から1つ選択)で実行します。
これは、ビルドを特定のコンピューターやコンピューターのセットで動かす必要がある場合に便利です。
例えば、ビルドにMacOS Xが必要な場合など。
<li>
複数の値を選択すると、構成マトリックスはそれらを含むように拡張され、
ビルドは選択したノードもしくはラベルで実行されます。例えば、Windows、LinuxおよびSolarisでテストをしたい場合に便利です。
</ul>
<br>
選択したノード名やラベル名は、ビルド実行中には、"label"という軸として利用可能です。
軸の値のアクセス方法の詳細については、"構成軸"のヘルプを参照してください。
</div>
<div>
Geef de nodes, waar uw bouwpoging uitgevoerd wordt, op.
<ul>
<li>
Indien niet opgegeven, zal Jenkins een beschikbare node kiezen voor het uitvoeren van uw bouwpoging.(Dit is hetzelfde gedrag als voor een project vrije stijl, indien niet vast aan een welbepaalde node voor uitvoering toegewezen.) Dit is handig indien uw projecten geen specifieke afhankelijkheden naar een welbepaalde node hebben. Het stelt Jenkins in staat om zijn nodes op een optimale manier te benutten.
<li>
Indien U wel een node selecteert, zal Jenkins uw bouwpogingen altijd op de opgegeven node uitvoeren. Op eenzelfde manier zal Jenkins ervoor zorgen dat uw pogingen altijd op ��n node van een groep nodes zal uitgevoerd worden, indien u een groepslabel gekozen hebt. Dit is natuurlijk hoogst nuttig indien bepaalde van uw projecten op een welbepaalde node of soort node uitgevoerd dienen te worden. Bvb. indien sommige van uw projecten op windows, Mac Os, linux,.... uitgevoerd moeten worden.
<li>
Indien U meerdere waarden selecteert, wordt de configuratiematrix zo opgesteld, dat uw bouwpogingen op alle geselecteerde nodes en/of groepen uitgevoerd wordt. Dit is handig indien je projecten hebt die op meerdere platformen gebouwd moeten worden. Denk maar aan applicaties die ontwikkeld worden voor zowel Linux, Mac OS, Solaris als Windows.
</ul>
<br>
Tijdens een bouwpoging, is de naam van de node of nodegroep, waarop de bouwpoging plaatsvindt, beschikbaar onder de "label" dimensie.
Zie de beschikbare hulppaginas over dimensies indien U meer te weten wenst te komen over de dimensies van een matrixproject.
</div>
<div>
Especifica os n&#243;s onde as constru&#231;&#245;es ser&#227;o executadas.
<ul>
<li>
Se nenhum for selecionado, Jenkins escolher&#225;
um n&#243; dispon&#237;vel para executar uma constru&#231;&#227;o (este &#233; o mesmo comportamento do projeto
estilo-livre quando "amarrar este projeto ao n&#243;" n&#227;o estiver selecionado). Isto &#233; &#250;til
quando o projeto n&#227;o tem depend&#234;ncia em nenhum n&#243; particular, assim
ele permite ao Jenkins utilizar n&#243;s em uma forma &#243;tima.
<li>
Se um for selecionado, Jenkins sempre executar&#225; a constru&#231;&#227;o no n&#243; especificado
(se voc&#234; selecionar um de "n&#243;s espec&#237;ficos")
ou um dos n&#243;s que pertencem ao label (se voc&#234; selecionar um de "label").
Isto &#233; &#250;til quando for necess&#225;rio que a constru&#231;&#227;o execute em um computador espec&#237;fico,
ou em um subconjunto de computadores. Por exemplo, talvez sua constru&#231;&#227;o necessite de um MacOS X.
<li>
Se valores m&#250;ltiplos forem selecionados, a matriz de configura&#231;&#227;o ser&#225; expandida para incluir
todos eles, e as constru&#231;&#245;s ser&#227;o executadas em todos os n&#243;s/labels selecionandos.
Isto &#233; &#250;lti, por exemplo, quando voc&#234; quiser executar testes em Windows, Linux,
e Solaris.
</ul>
<br>
Durante uma constru&#231;&#227;o, o nome do n&#243;/label selecionado est&#225; dispon&#237;vel como o eixo "label".
Veja a ajuda de "eixos" abaixo para mais informa&#231;&#245;es sobre como acessar o valor do eixo.
</div>
<div>
Выберите узлы, на которых будет производиться сборка.
<ul>
<li>
Если ничего не выбрано, Jenkins сам выберет доступный узел для выполнения сборки
(такое же поведение как и в случае проекта в свободной форме (free-style), когда
опция "Привязать проект к узлу" не включена). Это полезно когда проект не имеет
зависимости от конкретного узла и позволяет системе использовать узлы оптимальным
образом.
<li>
Если выбран один узел, то Jenkins всегда будет выполнять сборку только на указанном
узле (если вы выбрали один из конкретных узлов), либо один из узлов с указанной меткой
(если вы выбрали одну из "Меток").
Это может быть полезно в случае, если сборка должна быть запущена на конкретном
узле или наборе узлов. Например, возможно, ваша сборка требует MacOS X.
<li>
Если выбрано несколько значений, конфигурационная матрица будет расширена для
включения их всех и сборки будут производиться на всех выбранных узлах/метках.
Это может быть удобно, если ваш проект собирается либо тестируется на Windows,
Linux и Solaris.
</ul>
<br>
В процессе сборки выбранное имя узла/метки доступно как ось "labels".
Смотрите в подсказку к опции "Оси (axes)" ниже для получения более подробной информации о том как
получить значения по осям.
</div>
<div>
指定建置用的節點。
<ul>
<li>
如果沒有選取任何節點,Jenkins 會自動挑一個節點來建置
(行為模式就像是沒有使用「限定建置節點」的 Free-Style 專案)。
如果專案不一定要在哪些節點上才能建置,使用這個方式可以讓 Jenkins 充分利用每個節點。
<li>
如果選定某個節點,Jenkins 就只會在那個節點上建置 (假設您是在「獨立節點」中挑出來的),
或是在指定標籤中的任何一個節點上建置 (假如您是由「標籤」中選的)。
這種設定方式很適合只能在特定電腦上執行的建置作業。例如,一定要 MacOS X 才能建置的東西。
<li>
如果選取了一個以上的值,設定矩陣會將所有指定的項目納入展開,所有牽涉到的節點或標籤都會建置。
舉例來說,如果您想在 Windows, Linux 及 Solaris 上面都執行測試的話,這一招就很有用。
</ul>
<br>
建置時的節點或標籤名稱會顯示在「標籤」軸線上。
參考下方「座標軸」的說明,可以知道怎麼看座標值。
</div>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册