/******************************************************************************* * * Copyright (c) 2004-2010 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Tom Huybrechts * * *******************************************************************************/ package hudson.matrix; import hudson.Util; import hudson.security.ACL; 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 hudson.model.Hudson; 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 hudson.tasks.BuildWrapper; import hudson.tasks.Builder; import hudson.tasks.LogRotator; import hudson.tasks.Publisher; import java.io.IOException; 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); updateTransientActions(); } @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()); } /** * 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(); if (lb == null) { return 0; } int n = lb.getNumber(); if (!lb.isBuilding()) { n++; } 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(); } @Override public boolean isConfigurable() { return false; } @Override public boolean isConcurrentBuild() { return getParent().isConcurrentBuild(); } @Override protected Class<MatrixRun> getBuildClass() { return MatrixRun.class; } @Override protected MatrixRun newBuild() throws IOException { // for every MatrixRun there should be a parent MatrixBuild MatrixBuild lb = getParent().getLastBuild(); 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 = Util.join(combination.values(getParent().getAxes().subList(LabelAxis.class)), "&&"); return Hudson.getInstance().getLabel(Util.fixEmpty(expr)); } @Override public String getPronoun() { return Messages.MatrixConfiguration_Pronoun(); } @Override public JDK getJDK() { return Hudson.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(); } /** * @since 2.1.0 */ @Override public DescribableList<BuildWrapper, Descriptor<BuildWrapper>> getBuildWrappersList() { return getParent().getBuildWrappersList(); } @Override public Publisher getPublisher(Descriptor<Publisher> descriptor) { return getParent().getPublisher(descriptor); } @Override public LogRotator getLogRotator() { LogRotator lr = getParent().getLogRotator(); return new LinkedLogRotator(lr != null ? lr.getArtifactDaysToKeep() : -1, lr != null ? lr.getArtifactNumToKeep() : -1); } @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 setLogRotator(LogRotator logRotator) { //TODO fins the reason //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); } /** * @since 2.1.0 */ @Override public ACL getACL() { return getParent().getACL(); } /** * 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 * Hudson 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()); } /** * * @param parameters Can be null. */ public boolean scheduleBuild(ParametersAction parameters, Cause c) { return Hudson.getInstance().getQueue().schedule(this, getQuietPeriod(), parameters, new CauseAction(c)) != null; } }