/hudson-core/src/main/java/hudson/model/AbstractBuild.java

http://github.com/hudson/hudson · Java · 1140 lines · 620 code · 142 blank · 378 comment · 113 complexity · 7d9c0be91d36032b29de402a8e558589 MD5 · raw file

  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Yahoo! Inc., CloudBees, Inc.
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package hudson.model;
  25. import hudson.AbortException;
  26. import hudson.EnvVars;
  27. import hudson.Functions;
  28. import hudson.Launcher;
  29. import hudson.Util;
  30. import hudson.FilePath;
  31. import hudson.console.AnnotatedLargeText;
  32. import hudson.console.ExpandableDetailsNote;
  33. import hudson.slaves.WorkspaceList;
  34. import hudson.slaves.NodeProperty;
  35. import hudson.slaves.WorkspaceList.Lease;
  36. import hudson.matrix.MatrixConfiguration;
  37. import hudson.model.Fingerprint.BuildPtr;
  38. import hudson.model.Fingerprint.RangeSet;
  39. import hudson.model.listeners.SCMListener;
  40. import hudson.scm.ChangeLogParser;
  41. import hudson.scm.ChangeLogSet;
  42. import hudson.scm.ChangeLogSet.Entry;
  43. import hudson.scm.SCM;
  44. import hudson.scm.NullChangeLogParser;
  45. import hudson.tasks.BuildStep;
  46. import hudson.tasks.BuildWrapper;
  47. import hudson.tasks.Builder;
  48. import hudson.tasks.Fingerprinter.FingerprintAction;
  49. import hudson.tasks.Publisher;
  50. import hudson.tasks.BuildStepMonitor;
  51. import hudson.tasks.BuildTrigger;
  52. import hudson.tasks.test.AbstractTestResultAction;
  53. import hudson.util.AdaptedIterator;
  54. import hudson.util.Iterators;
  55. import hudson.util.LogTaskListener;
  56. import hudson.util.VariableResolver;
  57. import org.kohsuke.stapler.Stapler;
  58. import org.kohsuke.stapler.StaplerRequest;
  59. import org.kohsuke.stapler.StaplerResponse;
  60. import org.kohsuke.stapler.export.Exported;
  61. import org.xml.sax.SAXException;
  62. import javax.servlet.ServletException;
  63. import java.io.File;
  64. import java.io.IOException;
  65. import java.io.StringWriter;
  66. import java.util.AbstractSet;
  67. import java.util.ArrayList;
  68. import java.util.Calendar;
  69. import java.util.Collection;
  70. import java.util.Collections;
  71. import java.util.HashMap;
  72. import java.util.HashSet;
  73. import java.util.Iterator;
  74. import java.util.List;
  75. import java.util.Map;
  76. import java.util.Set;
  77. import java.util.logging.Level;
  78. import java.util.logging.Logger;
  79. /**
  80. * Base implementation of {@link Run}s that build software.
  81. *
  82. * For now this is primarily the common part of {@link Build} and MavenBuild.
  83. *
  84. * @author Kohsuke Kawaguchi
  85. * @see AbstractProject
  86. */
  87. public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends AbstractBuild<P,R>> extends Run<P,R> implements Queue.Executable {
  88. /**
  89. * Set if we want the blame information to flow from upstream to downstream build.
  90. */
  91. private static final boolean upstreamCulprits = Boolean.getBoolean("hudson.upstreamCulprits");
  92. /**
  93. * Name of the slave this project was built on.
  94. * Null or "" if built by the master. (null happens when we read old record that didn't have this information.)
  95. */
  96. private String builtOn;
  97. /**
  98. * The file path on the node that performed a build. Kept as a string since {@link FilePath} is not serializable into XML.
  99. * @since 1.319
  100. */
  101. private String workspace;
  102. /**
  103. * Version of Hudson that built this.
  104. */
  105. private String hudsonVersion;
  106. /**
  107. * SCM used for this build.
  108. * Maybe null, for historical reason, in which case CVS is assumed.
  109. */
  110. private ChangeLogParser scm;
  111. /**
  112. * Changes in this build.
  113. */
  114. private volatile transient ChangeLogSet<? extends Entry> changeSet;
  115. /**
  116. * Cumulative list of people who contributed to the build problem.
  117. *
  118. * <p>
  119. * This is a list of {@link User#getId() user ids} who made a change
  120. * since the last non-broken build. Can be null (which should be
  121. * treated like empty set), because of the compatibility.
  122. *
  123. * <p>
  124. * This field is semi-final --- once set the value will never be modified.
  125. *
  126. * @since 1.137
  127. */
  128. private volatile Set<String> culprits;
  129. /**
  130. * During the build this field remembers {@link BuildWrapper.Environment}s created by
  131. * {@link BuildWrapper}. This design is bit ugly but forced due to compatibility.
  132. */
  133. protected transient List<Environment> buildEnvironments;
  134. protected AbstractBuild(P job) throws IOException {
  135. super(job);
  136. }
  137. protected AbstractBuild(P job, Calendar timestamp) {
  138. super(job, timestamp);
  139. }
  140. protected AbstractBuild(P project, File buildDir) throws IOException {
  141. super(project, buildDir);
  142. }
  143. public final P getProject() {
  144. return getParent();
  145. }
  146. /**
  147. * Returns a {@link Slave} on which this build was done.
  148. *
  149. * @return
  150. * null, for example if the slave that this build run no longer exists.
  151. */
  152. public Node getBuiltOn() {
  153. if (builtOn==null || builtOn.equals(""))
  154. return Hudson.getInstance();
  155. else
  156. return Hudson.getInstance().getNode(builtOn);
  157. }
  158. /**
  159. * Returns the name of the slave it was built on; null or "" if built by the master.
  160. * (null happens when we read old record that didn't have this information.)
  161. */
  162. @Exported(name="builtOn")
  163. public String getBuiltOnStr() {
  164. return builtOn;
  165. }
  166. /**
  167. * Used to render the side panel "Back to project" link.
  168. *
  169. * <p>
  170. * In a rare situation where a build can be reached from multiple paths,
  171. * returning different URLs from this method based on situations might
  172. * be desirable.
  173. *
  174. * <p>
  175. * If you override this method, you'll most likely also want to override
  176. * {@link #getDisplayName()}.
  177. */
  178. public String getUpUrl() {
  179. return Functions.getNearestAncestorUrl(Stapler.getCurrentRequest(),getParent())+'/';
  180. }
  181. /**
  182. * Gets the directory where this build is being built.
  183. *
  184. * <p>
  185. * Note to implementors: to control where the workspace is created, override
  186. * {@link AbstractRunner#decideWorkspace(Node,WorkspaceList)}.
  187. *
  188. * @return
  189. * null if the workspace is on a slave that's not connected. Note that once the build is completed,
  190. * the workspace may be used to build something else, so the value returned from this method may
  191. * no longer show a workspace as it was used for this build.
  192. * @since 1.319
  193. */
  194. public final FilePath getWorkspace() {
  195. if (workspace==null) return null;
  196. Node n = getBuiltOn();
  197. if (n==null) return null;
  198. return n.createPath(workspace);
  199. }
  200. /**
  201. * Normally, a workspace is assigned by {@link Runner}, but this lets you set the workspace in case
  202. * {@link AbstractBuild} is created without a build.
  203. */
  204. protected void setWorkspace(FilePath ws) {
  205. this.workspace = ws.getRemote();
  206. }
  207. /**
  208. * Returns the root directory of the checked-out module.
  209. * <p>
  210. * This is usually where <tt>pom.xml</tt>, <tt>build.xml</tt>
  211. * and so on exists.
  212. */
  213. public final FilePath getModuleRoot() {
  214. FilePath ws = getWorkspace();
  215. if (ws==null) return null;
  216. return getParent().getScm().getModuleRoot(ws,this);
  217. }
  218. /**
  219. * Returns the root directories of all checked-out modules.
  220. * <p>
  221. * Some SCMs support checking out multiple modules into the same workspace.
  222. * In these cases, the returned array will have a length greater than one.
  223. * @return The roots of all modules checked out from the SCM.
  224. */
  225. public FilePath[] getModuleRoots() {
  226. FilePath ws = getWorkspace();
  227. if (ws==null) return null;
  228. return getParent().getScm().getModuleRoots(ws,this);
  229. }
  230. /**
  231. * List of users who committed a change since the last non-broken build till now.
  232. *
  233. * <p>
  234. * This list at least always include people who made changes in this build, but
  235. * if the previous build was a failure it also includes the culprit list from there.
  236. * Culprits of unstable build are also included
  237. * see <a href="http://issues.hudson-ci.org/browse/HUDSON-4617">HUDSON-4617</a> for details
  238. * @return
  239. * can be empty but never null.
  240. */
  241. @Exported
  242. public Set<User> getCulprits() {
  243. if (culprits==null) {
  244. Set<User> r = new HashSet<User>();
  245. R p = getPreviousCompletedBuild();
  246. if (p !=null && isBuilding()) {
  247. Result pr = p.getResult();
  248. if (pr!=null && pr.isWorseOrEqualTo(Result.UNSTABLE)) {
  249. // we are still building, so this is just the current latest information,
  250. // but we seems to be failing so far, so inherit culprits from the previous build.
  251. // isBuilding() check is to avoid recursion when loading data from old Hudson, which doesn't record
  252. // this information
  253. r.addAll(p.getCulprits());
  254. }
  255. }
  256. for (Entry e : getChangeSet())
  257. r.add(e.getAuthor());
  258. if (upstreamCulprits) {
  259. // If we have dependencies since the last successful build, add their authors to our list
  260. R previousBuild = getPreviousSuccessfulBuild();
  261. if (previousBuild != null) {
  262. Map <AbstractProject,AbstractBuild.DependencyChange> depmap = getDependencyChanges(previousBuild);
  263. for (AbstractBuild.DependencyChange dep : depmap.values()) {
  264. for (AbstractBuild<?,?> b : dep.getBuilds()) {
  265. for (Entry entry : b.getChangeSet()) {
  266. r.add(entry.getAuthor());
  267. }
  268. }
  269. }
  270. }
  271. }
  272. return r;
  273. }
  274. return new AbstractSet<User>() {
  275. public Iterator<User> iterator() {
  276. return new AdaptedIterator<String,User>(culprits.iterator()) {
  277. protected User adapt(String id) {
  278. return User.get(id);
  279. }
  280. };
  281. }
  282. public int size() {
  283. return culprits.size();
  284. }
  285. };
  286. }
  287. /**
  288. * Returns true if this user has made a commit to this build.
  289. *
  290. * @since 1.191
  291. */
  292. public boolean hasParticipant(User user) {
  293. for (ChangeLogSet.Entry e : getChangeSet())
  294. if (e.getAuthor()==user)
  295. return true;
  296. return false;
  297. }
  298. /**
  299. * Gets the version of Hudson that was used to build this job.
  300. *
  301. * @since 1.246
  302. */
  303. public String getHudsonVersion() {
  304. return hudsonVersion;
  305. }
  306. @Override
  307. public synchronized void delete() throws IOException {
  308. // Need to check if deleting this build affects lastSuccessful/lastStable symlinks
  309. R lastSuccessful = getProject().getLastSuccessfulBuild(),
  310. lastStable = getProject().getLastStableBuild();
  311. super.delete();
  312. try {
  313. if (lastSuccessful == this)
  314. updateSymlink("lastSuccessful", getProject().getLastSuccessfulBuild());
  315. if (lastStable == this)
  316. updateSymlink("lastStable", getProject().getLastStableBuild());
  317. } catch (InterruptedException ex) {
  318. LOGGER.warning("Interrupted update of lastSuccessful/lastStable symlinks for "
  319. + getProject().getDisplayName());
  320. // handle it later
  321. Thread.currentThread().interrupt();
  322. }
  323. }
  324. private void updateSymlink(String name, AbstractBuild<?,?> newTarget) throws InterruptedException {
  325. if (newTarget != null)
  326. newTarget.createSymlink(new LogTaskListener(LOGGER, Level.WARNING), name);
  327. else
  328. new File(getProject().getBuildDir(), "../"+name).delete();
  329. }
  330. private void createSymlink(TaskListener listener, String name) throws InterruptedException {
  331. Util.createSymlink(getProject().getBuildDir(),"builds/"+getId(),"../"+name,listener);
  332. }
  333. protected abstract class AbstractRunner extends Runner {
  334. /**
  335. * Since configuration can be changed while a build is in progress,
  336. * create a launcher once and stick to it for the entire build duration.
  337. */
  338. protected Launcher launcher;
  339. /**
  340. * Output/progress of this build goes here.
  341. */
  342. protected BuildListener listener;
  343. /**
  344. * Returns the current {@link Node} on which we are buildling.
  345. */
  346. protected final Node getCurrentNode() {
  347. return Executor.currentExecutor().getOwner().getNode();
  348. }
  349. /**
  350. * Allocates the workspace from {@link WorkspaceList}.
  351. *
  352. * @param n
  353. * Passed in for the convenience. The node where the build is running.
  354. * @param wsl
  355. * Passed in for the convenience. The returned path must be registered to this object.
  356. */
  357. protected Lease decideWorkspace(Node n, WorkspaceList wsl) throws InterruptedException, IOException {
  358. // TODO: this cast is indicative of abstraction problem
  359. return wsl.allocate(n.getWorkspaceFor((TopLevelItem)getProject()));
  360. }
  361. public Result run(BuildListener listener) throws Exception {
  362. Node node = getCurrentNode();
  363. assert builtOn==null;
  364. builtOn = node.getNodeName();
  365. hudsonVersion = Hudson.VERSION;
  366. this.listener = listener;
  367. launcher = createLauncher(listener);
  368. if (!Hudson.getInstance().getNodes().isEmpty())
  369. listener.getLogger().println(node instanceof Hudson ? Messages.AbstractBuild_BuildingOnMaster() : Messages.AbstractBuild_BuildingRemotely(builtOn));
  370. final Lease lease = decideWorkspace(node,Computer.currentComputer().getWorkspaceList());
  371. try {
  372. workspace = lease.path.getRemote();
  373. node.getFileSystemProvisioner().prepareWorkspace(AbstractBuild.this,lease.path,listener);
  374. if (project.isCleanWorkspaceRequired()) {
  375. listener.getLogger().println("Cleaning the workspace because project is configured to clean the workspace before each build.");
  376. if (!project.cleanWorkspace()){
  377. listener.getLogger().println("Workspace cleaning was attempted but SCM blocked the cleaning.");
  378. }
  379. }
  380. checkout(listener);
  381. if (!preBuild(listener,project.getProperties()))
  382. return Result.FAILURE;
  383. Result result = doRun(listener);
  384. Computer c = node.toComputer();
  385. if (c==null || c.isOffline()) {
  386. // As can be seen in HUDSON-5073, when a build fails because of the slave connectivity problem,
  387. // error message doesn't point users to the slave. So let's do it here.
  388. listener.hyperlink("/computer/"+builtOn+"/log","Looks like the node went offline during the build. Check the slave log for the details.");
  389. // grab the end of the log file. This might not work very well if the slave already
  390. // starts reconnecting. Fixing this requires a ring buffer in slave logs.
  391. AnnotatedLargeText<Computer> log = c.getLogText();
  392. StringWriter w = new StringWriter();
  393. log.writeHtmlTo(Math.max(0,c.getLogFile().length()-10240),w);
  394. listener.getLogger().print(ExpandableDetailsNote.encodeTo("details",w.toString()));
  395. listener.getLogger().println();
  396. }
  397. // kill run-away processes that are left
  398. // use multiple environment variables so that people can escape this massacre by overriding an environment
  399. // variable for some processes
  400. launcher.kill(getCharacteristicEnvVars());
  401. // this is ugly, but for historical reason, if non-null value is returned
  402. // it should become the final result.
  403. if (result==null) result = getResult();
  404. if (result==null) result = Result.SUCCESS;
  405. return result;
  406. } finally {
  407. lease.release();
  408. this.listener = null;
  409. }
  410. }
  411. /**
  412. * Creates a {@link Launcher} that this build will use. This can be overridden by derived types
  413. * to decorate the resulting {@link Launcher}.
  414. *
  415. * @param listener
  416. * Always non-null. Connected to the main build output.
  417. */
  418. protected Launcher createLauncher(BuildListener listener) throws IOException, InterruptedException {
  419. Launcher l = getCurrentNode().createLauncher(listener);
  420. if (project instanceof BuildableItemWithBuildWrappers) {
  421. BuildableItemWithBuildWrappers biwbw = (BuildableItemWithBuildWrappers) project;
  422. for (BuildWrapper bw : biwbw.getBuildWrappersList())
  423. l = bw.decorateLauncher(AbstractBuild.this,l,listener);
  424. }
  425. buildEnvironments = new ArrayList<Environment>();
  426. for (NodeProperty nodeProperty: Hudson.getInstance().getGlobalNodeProperties()) {
  427. Environment environment = nodeProperty.setUp(AbstractBuild.this, l, listener);
  428. if (environment != null) {
  429. buildEnvironments.add(environment);
  430. }
  431. }
  432. for (NodeProperty nodeProperty: Computer.currentComputer().getNode().getNodeProperties()) {
  433. Environment environment = nodeProperty.setUp(AbstractBuild.this, l, listener);
  434. if (environment != null) {
  435. buildEnvironments.add(environment);
  436. }
  437. }
  438. return l;
  439. }
  440. private void checkout(BuildListener listener) throws Exception {
  441. for (int retryCount = project.getScmCheckoutRetryCount(); ; retryCount--) {
  442. // for historical reasons, null in the scm field means CVS, so we need to explicitly set this to something
  443. // in case check out fails and leaves a broken changelog.xml behind.
  444. // see http://www.nabble.com/CVSChangeLogSet.parse-yields-SAXParseExceptions-when-parsing-bad-*AccuRev*-changelog.xml-files-td22213663.html
  445. AbstractBuild.this.scm = new NullChangeLogParser();
  446. try {
  447. if (project.checkout(AbstractBuild.this, launcher, listener,
  448. new File(getRootDir(), "changelog.xml"))) {
  449. // check out succeeded
  450. SCM scm = project.getScm();
  451. AbstractBuild.this.scm = scm.createChangeLogParser();
  452. AbstractBuild.this.changeSet = AbstractBuild.this.calcChangeSet();
  453. for (SCMListener l : Hudson.getInstance().getSCMListeners()) {
  454. l.onChangeLogParsed(AbstractBuild.this, listener, changeSet);
  455. }
  456. return;
  457. }
  458. } catch (AbortException e) {
  459. listener.error(e.getMessage());
  460. } catch (IOException e) {
  461. // checkout error not yet reported
  462. e.printStackTrace(listener.getLogger());
  463. }
  464. // all attempts failed
  465. if (retryCount == 0){
  466. throw new RunnerAbortedException();
  467. }
  468. listener.getLogger().println("Retrying after 10 seconds");
  469. Thread.sleep(10000);
  470. }
  471. }
  472. /**
  473. * The portion of a build that is specific to a subclass of {@link AbstractBuild}
  474. * goes here.
  475. *
  476. * @return
  477. * null to continue the build normally (that means the doRun method
  478. * itself run successfully)
  479. * Return a non-null value to abort the build right there with the specified result code.
  480. */
  481. protected abstract Result doRun(BuildListener listener) throws Exception, RunnerAbortedException;
  482. /**
  483. * @see #post(BuildListener)
  484. */
  485. protected abstract void post2(BuildListener listener) throws Exception;
  486. public final void post(BuildListener listener) throws Exception {
  487. try {
  488. post2(listener);
  489. //Resolve issue with invalid symlinks for maven (see http://issues.hudson-ci.org/browse/HUDSON-8340)
  490. if (getResult().isBetterOrEqualTo(Result.UNSTABLE))
  491. createSymlink(listener, "lastSuccessful");
  492. if (getResult().isBetterOrEqualTo(Result.SUCCESS))
  493. createSymlink(listener, "lastStable");
  494. } finally {
  495. // update the culprit list
  496. HashSet<String> r = new HashSet<String>();
  497. for (User u : getCulprits())
  498. r.add(u.getId());
  499. culprits = r;
  500. CheckPoint.CULPRITS_DETERMINED.report();
  501. }
  502. }
  503. public void cleanUp(BuildListener listener) throws Exception {
  504. BuildTrigger.execute(AbstractBuild.this, listener);
  505. buildEnvironments = null;
  506. }
  507. /**
  508. * @deprecated as of 1.356
  509. * Use {@link #performAllBuildSteps(BuildListener, Map, boolean)}
  510. */
  511. protected final void performAllBuildStep(BuildListener listener, Map<?,? extends BuildStep> buildSteps, boolean phase) throws InterruptedException, IOException {
  512. performAllBuildSteps(listener,buildSteps.values(),phase);
  513. }
  514. protected final boolean performAllBuildSteps(BuildListener listener, Map<?,? extends BuildStep> buildSteps, boolean phase) throws InterruptedException, IOException {
  515. return performAllBuildSteps(listener,buildSteps.values(),phase);
  516. }
  517. /**
  518. * @deprecated as of 1.356
  519. * Use {@link #performAllBuildSteps(BuildListener, Iterable, boolean)}
  520. */
  521. protected final void performAllBuildStep(BuildListener listener, Iterable<? extends BuildStep> buildSteps, boolean phase) throws InterruptedException, IOException {
  522. performAllBuildSteps(listener,buildSteps,phase);
  523. }
  524. /**
  525. * Runs all the given build steps, even if one of them fail.
  526. *
  527. * @param phase
  528. * true for the post build processing, and false for the final "run after finished" execution.
  529. */
  530. protected final boolean performAllBuildSteps(BuildListener listener, Iterable<? extends BuildStep> buildSteps,
  531. boolean phase) throws InterruptedException, IOException {
  532. boolean r = true;
  533. for (BuildStep bs : buildSteps) {
  534. if (bs instanceof Publisher && ((Publisher) bs).needsToRun(getResult()) &&
  535. ((((Publisher) bs).needsToRunAfterFinalized()) ^ phase)) {
  536. try {
  537. r &= perform(bs, listener);
  538. } catch (Exception e) {
  539. String msg = "Publisher " + bs.getClass().getName() + " aborted due to exception";
  540. e.printStackTrace(listener.error(msg));
  541. LOGGER.log(Level.WARNING, msg, e);
  542. setResult(Result.FAILURE);
  543. }
  544. }
  545. }
  546. return r;
  547. }
  548. /**
  549. * Calls a build step.
  550. */
  551. protected final boolean perform(BuildStep bs, BuildListener listener) throws InterruptedException, IOException {
  552. BuildStepMonitor mon;
  553. try {
  554. mon = bs.getRequiredMonitorService();
  555. } catch (AbstractMethodError e) {
  556. mon = BuildStepMonitor.BUILD;
  557. }
  558. return mon.perform(bs, AbstractBuild.this, launcher, listener);
  559. }
  560. protected final boolean preBuild(BuildListener listener,Map<?,? extends BuildStep> steps) {
  561. return preBuild(listener,steps.values());
  562. }
  563. protected final boolean preBuild(BuildListener listener,Collection<? extends BuildStep> steps) {
  564. return preBuild(listener,(Iterable<? extends BuildStep>)steps);
  565. }
  566. protected final boolean preBuild(BuildListener listener,Iterable<? extends BuildStep> steps) {
  567. for (BuildStep bs : steps)
  568. if (!bs.prebuild(AbstractBuild.this,listener))
  569. return false;
  570. return true;
  571. }
  572. }
  573. /**
  574. * Gets the changes incorporated into this build.
  575. *
  576. * @return never null.
  577. */
  578. @Exported
  579. public ChangeLogSet<? extends Entry> getChangeSet() {
  580. if (scm==null) {
  581. // for historical reason, null means CVS.
  582. try {
  583. Class<?> c = Hudson.getInstance().getPluginManager().uberClassLoader.loadClass("hudson.scm.CVSChangeLogParser");
  584. scm = (ChangeLogParser)c.newInstance();
  585. } catch (ClassNotFoundException e) {
  586. // if CVS isn't available, fall back to something non-null.
  587. scm = new NullChangeLogParser();
  588. } catch (InstantiationException e) {
  589. scm = new NullChangeLogParser();
  590. throw (Error)new InstantiationError().initCause(e);
  591. } catch (IllegalAccessException e) {
  592. scm = new NullChangeLogParser();
  593. throw (Error)new IllegalAccessError().initCause(e);
  594. }
  595. }
  596. if (changeSet==null) // cached value
  597. try {
  598. changeSet = calcChangeSet();
  599. } finally {
  600. // defensive check. if the calculation fails (such as through an exception),
  601. // set a dummy value so that it'll work the next time. the exception will
  602. // be still reported, giving the plugin developer an opportunity to fix it.
  603. if (changeSet==null)
  604. changeSet=ChangeLogSet.createEmpty(this);
  605. }
  606. return changeSet;
  607. }
  608. /**
  609. * Returns true if the changelog is already computed.
  610. */
  611. public boolean hasChangeSetComputed() {
  612. File changelogFile = new File(getRootDir(), "changelog.xml");
  613. return changelogFile.exists();
  614. }
  615. private ChangeLogSet<? extends Entry> calcChangeSet() {
  616. File changelogFile = new File(getRootDir(), "changelog.xml");
  617. if (!changelogFile.exists())
  618. return ChangeLogSet.createEmpty(this);
  619. try {
  620. return scm.parse(this,changelogFile);
  621. } catch (IOException e) {
  622. e.printStackTrace();
  623. } catch (SAXException e) {
  624. e.printStackTrace();
  625. }
  626. return ChangeLogSet.createEmpty(this);
  627. }
  628. @Override
  629. public EnvVars getEnvironment(TaskListener log) throws IOException, InterruptedException {
  630. EnvVars env = super.getEnvironment(log);
  631. FilePath ws = getWorkspace();
  632. if (ws!=null) // if this is done very early on in the build, workspace may not be decided yet. see HUDSON-3997
  633. env.put("WORKSPACE", ws.getRemote());
  634. // servlet container may have set CLASSPATH in its launch script,
  635. // so don't let that inherit to the new child process.
  636. // see http://www.nabble.com/Run-Job-with-JDK-1.4.2-tf4468601.html
  637. env.put("CLASSPATH","");
  638. JDK jdk = project.getJDK();
  639. if (jdk != null) {
  640. Computer computer = Computer.currentComputer();
  641. if (computer != null) { // just in case were not in a build
  642. jdk = jdk.forNode(computer.getNode(), log);
  643. }
  644. jdk.buildEnvVars(env);
  645. }
  646. project.getScm().buildEnvVars(this,env);
  647. if (buildEnvironments!=null)
  648. for (Environment e : buildEnvironments)
  649. e.buildEnvVars(env);
  650. for (EnvironmentContributingAction a : Util.filter(getActions(),EnvironmentContributingAction.class))
  651. a.buildEnvVars(this,env);
  652. EnvVars.resolve(env);
  653. return env;
  654. }
  655. public Calendar due() {
  656. return getTimestamp();
  657. }
  658. /**
  659. * Builds up a set of variable names that contain sensitive values that
  660. * should not be exposed. The expection is that this set is populated with
  661. * keys returned by {@link #getBuildVariables()} that should have their
  662. * values masked for display purposes.
  663. *
  664. * @since 1.378
  665. */
  666. public Set<String> getSensitiveBuildVariables() {
  667. Set<String> s = new HashSet<String>();
  668. ParametersAction parameters = getAction(ParametersAction.class);
  669. if (parameters != null) {
  670. for (ParameterValue p : parameters) {
  671. if (p.isSensitive()) {
  672. s.add(p.getName());
  673. }
  674. }
  675. }
  676. // Allow BuildWrappers to determine if any of their data is sensitive
  677. if (project instanceof BuildableItemWithBuildWrappers) {
  678. for (BuildWrapper bw : ((BuildableItemWithBuildWrappers) project).getBuildWrappersList()) {
  679. bw.makeSensitiveBuildVariables(this, s);
  680. }
  681. }
  682. return s;
  683. }
  684. /**
  685. * Provides additional variables and their values to {@link Builder}s.
  686. *
  687. * <p>
  688. * This mechanism is used by {@link MatrixConfiguration} to pass
  689. * the configuration values to the current build. It is up to
  690. * {@link Builder}s to decide whether it wants to recognize the values
  691. * or how to use them.
  692. *
  693. * <p>
  694. * This also includes build parameters if a build is parameterized.
  695. *
  696. * @return
  697. * The returned map is mutable so that subtypes can put more values.
  698. */
  699. public Map<String,String> getBuildVariables() {
  700. Map<String,String> r = new HashMap<String, String>();
  701. ParametersAction parameters = getAction(ParametersAction.class);
  702. if (parameters!=null) {
  703. // this is a rather round about way of doing this...
  704. for (ParameterValue p : parameters) {
  705. String v = p.createVariableResolver(this).resolve(p.getName());
  706. if (v!=null) r.put(p.getName(),v);
  707. }
  708. }
  709. customizeBuildVariables(r);
  710. // allow the BuildWrappers to contribute additional build variables
  711. if (project instanceof BuildableItemWithBuildWrappers) {
  712. for (BuildWrapper bw : ((BuildableItemWithBuildWrappers) project).getBuildWrappersList())
  713. bw.makeBuildVariables(this,r);
  714. }
  715. return r;
  716. }
  717. /**
  718. * @since 2.1.0
  719. */
  720. protected void customizeBuildVariables(final Map<String, String> vars) {
  721. // nop
  722. }
  723. /**
  724. * Creates {@link VariableResolver} backed by {@link #getBuildVariables()}.
  725. */
  726. public final VariableResolver<String> getBuildVariableResolver() {
  727. return new VariableResolver.ByMap<String>(getBuildVariables());
  728. }
  729. /**
  730. * Gets {@link AbstractTestResultAction} associated with this build if any.
  731. */
  732. public AbstractTestResultAction getTestResultAction() {
  733. return getAction(AbstractTestResultAction.class);
  734. }
  735. /**
  736. * Invoked by {@link Executor} to performs a build.
  737. */
  738. public abstract void run();
  739. //
  740. //
  741. // fingerprint related stuff
  742. //
  743. //
  744. @Override
  745. public String getWhyKeepLog() {
  746. // if any of the downstream project is configured with 'keep dependency component',
  747. // we need to keep this log
  748. OUTER:
  749. for (AbstractProject<?,?> p : getParent().getDownstreamProjects()) {
  750. if (!p.isKeepDependencies()) continue;
  751. AbstractBuild<?,?> fb = p.getFirstBuild();
  752. if (fb==null) continue; // no active record
  753. // is there any active build that depends on us?
  754. for (int i : getDownstreamRelationship(p).listNumbersReverse()) {
  755. // TODO: this is essentially a "find intersection between two sparse sequences"
  756. // and we should be able to do much better.
  757. if (i<fb.getNumber())
  758. continue OUTER; // all the other records are younger than the first record, so pointless to search.
  759. AbstractBuild<?,?> b = p.getBuildByNumber(i);
  760. if (b!=null)
  761. return Messages.AbstractBuild_KeptBecause(b);
  762. }
  763. }
  764. return super.getWhyKeepLog();
  765. }
  766. /**
  767. * Gets the dependency relationship from this build (as the source)
  768. * and that project (as the sink.)
  769. *
  770. * @return
  771. * range of build numbers that represent which downstream builds are using this build.
  772. * The range will be empty if no build of that project matches this, but it'll never be null.
  773. */
  774. public RangeSet getDownstreamRelationship(AbstractProject that) {
  775. RangeSet rs = new RangeSet();
  776. FingerprintAction f = getAction(FingerprintAction.class);
  777. if (f==null) return rs;
  778. // look for fingerprints that point to this build as the source, and merge them all
  779. for (Fingerprint e : f.getFingerprints().values()) {
  780. if (upstreamCulprits) {
  781. // With upstreamCulprits, we allow downstream relationships
  782. // from intermediate jobs
  783. rs.add(e.getRangeSet(that));
  784. } else {
  785. BuildPtr o = e.getOriginal();
  786. if (o!=null && o.is(this))
  787. rs.add(e.getRangeSet(that));
  788. }
  789. }
  790. return rs;
  791. }
  792. /**
  793. * Works like {@link #getDownstreamRelationship(AbstractProject)} but returns
  794. * the actual build objects, in ascending order.
  795. * @since 1.150
  796. */
  797. public Iterable<AbstractBuild<?,?>> getDownstreamBuilds(final AbstractProject<?,?> that) {
  798. final Iterable<Integer> nums = getDownstreamRelationship(that).listNumbers();
  799. return new Iterable<AbstractBuild<?, ?>>() {
  800. public Iterator<AbstractBuild<?, ?>> iterator() {
  801. return Iterators.removeNull(
  802. new AdaptedIterator<Integer,AbstractBuild<?,?>>(nums) {
  803. protected AbstractBuild<?, ?> adapt(Integer item) {
  804. return that.getBuildByNumber(item);
  805. }
  806. });
  807. }
  808. };
  809. }
  810. /**
  811. * Gets the dependency relationship from this build (as the sink)
  812. * and that project (as the source.)
  813. *
  814. * @return
  815. * Build number of the upstream build that feed into this build,
  816. * or -1 if no record is available.
  817. */
  818. public int getUpstreamRelationship(AbstractProject that) {
  819. FingerprintAction f = getAction(FingerprintAction.class);
  820. if (f==null) return -1;
  821. int n = -1;
  822. // look for fingerprints that point to the given project as the source, and merge them all
  823. for (Fingerprint e : f.getFingerprints().values()) {
  824. if (upstreamCulprits) {
  825. // With upstreamCulprits, we allow upstream relationships
  826. // from intermediate jobs
  827. Fingerprint.RangeSet rangeset = e.getRangeSet(that);
  828. if (!rangeset.isEmpty()) {
  829. n = Math.max(n, rangeset.listNumbersReverse().iterator().next());
  830. }
  831. } else {
  832. BuildPtr o = e.getOriginal();
  833. if (o!=null && o.belongsTo(that))
  834. n = Math.max(n,o.getNumber());
  835. }
  836. }
  837. return n;
  838. }
  839. /**
  840. * Works like {@link #getUpstreamRelationship(AbstractProject)} but returns the
  841. * actual build object.
  842. *
  843. * @return
  844. * null if no such upstream build was found, or it was found but the
  845. * build record is already lost.
  846. */
  847. public AbstractBuild<?,?> getUpstreamRelationshipBuild(AbstractProject<?,?> that) {
  848. int n = getUpstreamRelationship(that);
  849. if (n==-1) return null;
  850. return that.getBuildByNumber(n);
  851. }
  852. /**
  853. * Gets the downstream builds of this build, which are the builds of the
  854. * downstream projects that use artifacts of this build.
  855. *
  856. * @return
  857. * For each project with fingerprinting enabled, returns the range
  858. * of builds (which can be empty if no build uses the artifact from this build.)
  859. */
  860. public Map<AbstractProject,RangeSet> getDownstreamBuilds() {
  861. Map<AbstractProject,RangeSet> r = new HashMap<AbstractProject,RangeSet>();
  862. for (AbstractProject p : getParent().getDownstreamProjects()) {
  863. if (p.isFingerprintConfigured())
  864. r.put(p,getDownstreamRelationship(p));
  865. }
  866. return r;
  867. }
  868. /**
  869. * Gets the upstream builds of this build, which are the builds of the
  870. * upstream projects whose artifacts feed into this build.
  871. *
  872. * @see #getTransitiveUpstreamBuilds()
  873. */
  874. public Map<AbstractProject,Integer> getUpstreamBuilds() {
  875. return _getUpstreamBuilds(getParent().getUpstreamProjects());
  876. }
  877. /**
  878. * Works like {@link #getUpstreamBuilds()} but also includes all the transitive
  879. * dependencies as well.
  880. */
  881. public Map<AbstractProject,Integer> getTransitiveUpstreamBuilds() {
  882. return _getUpstreamBuilds(getParent().getTransitiveUpstreamProjects());
  883. }
  884. private Map<AbstractProject, Integer> _getUpstreamBuilds(Collection<AbstractProject> projects) {
  885. Map<AbstractProject,Integer> r = new HashMap<AbstractProject,Integer>();
  886. for (AbstractProject p : projects) {
  887. int n = getUpstreamRelationship(p);
  888. if (n>=0)
  889. r.put(p,n);
  890. }
  891. return r;
  892. }
  893. /**
  894. * Gets the changes in the dependency between the given build and this build.
  895. */
  896. public Map<AbstractProject,DependencyChange> getDependencyChanges(AbstractBuild from) {
  897. if (from==null) return Collections.emptyMap(); // make it easy to call this from views
  898. FingerprintAction n = this.getAction(FingerprintAction.class);
  899. FingerprintAction o = from.getAction(FingerprintAction.class);
  900. if (n==null || o==null) return Collections.emptyMap();
  901. Map<AbstractProject,Integer> ndep = n.getDependencies();
  902. Map<AbstractProject,Integer> odep = o.getDependencies();
  903. Map<AbstractProject,DependencyChange> r = new HashMap<AbstractProject,DependencyChange>();
  904. for (Map.Entry<AbstractProject,Integer> entry : odep.entrySet()) {
  905. AbstractProject p = entry.getKey();
  906. Integer oldNumber = entry.getValue();
  907. Integer newNumber = ndep.get(p);
  908. if (newNumber!=null && oldNumber.compareTo(newNumber)<0) {
  909. r.put(p,new DependencyChange(p,oldNumber,newNumber));
  910. }
  911. }
  912. return r;
  913. }
  914. /**
  915. * Represents a change in the dependency.
  916. */
  917. public static final class DependencyChange {
  918. /**
  919. * The dependency project.
  920. */
  921. //TODO: review and check whether we can do it private
  922. public final AbstractProject project;
  923. /**
  924. * Version of the dependency project used in the previous build.
  925. */
  926. //TODO: review and check whether we can do it private
  927. public final int fromId;
  928. /**
  929. * {@link Build} object for {@link #fromId}. Can be null if the log is gone.
  930. */
  931. //TODO: review and check whether we can do it private
  932. public final AbstractBuild from;
  933. /**
  934. * Version of the dependency project used in this build.
  935. */
  936. //TODO: review and check whether we can do it private
  937. public final int toId;
  938. //TODO: review and check whether we can do it private
  939. public final AbstractBuild to;
  940. public DependencyChange(AbstractProject<?,?> project, int fromId, int toId) {
  941. this.project = project;
  942. this.fromId = fromId;
  943. this.toId = toId;
  944. this.from = project.getBuildByNumber(fromId);
  945. this.to = project.getBuildByNumber(toId);
  946. }
  947. public AbstractProject getProject() {
  948. return project;
  949. }
  950. public int getFromId() {
  951. return fromId;
  952. }
  953. public AbstractBuild getFrom() {
  954. return from;
  955. }
  956. public int getToId() {
  957. return toId;
  958. }
  959. public AbstractBuild getTo() {
  960. return to;
  961. }
  962. /**
  963. * Gets the {@link AbstractBuild} objects (fromId,toId].
  964. * <p>
  965. * This method returns all such available builds in the ascending order
  966. * of IDs, but due to log rotations, some builds may be already unavailable.
  967. */
  968. public List<AbstractBuild> getBuilds() {
  969. List<AbstractBuild> r = new ArrayList<AbstractBuild>();
  970. AbstractBuild<?,?> b = (AbstractBuild)project.getNearestBuild(fromId);
  971. if (b!=null && b.getNumber()==fromId)
  972. b = b.getNextBuild(); // fromId exclusive
  973. while (b!=null && b.getNumber()<=toId) {
  974. r.add(b);
  975. b = b.getNextBuild();
  976. }
  977. return r;
  978. }
  979. }
  980. //
  981. // web methods
  982. //
  983. /**
  984. * Stops this build if it's still going.
  985. *
  986. * If we use this/executor/stop URL, it causes 404 if the build is already killed,
  987. * as {@link #getExecutor()} returns null.
  988. */
  989. public synchronized void doStop(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
  990. Executor e = getExecutor();
  991. if (e!=null)
  992. e.doStop(req,rsp);
  993. else
  994. // nothing is building
  995. rsp.forwardToPreviousPage(req);
  996. }
  997. private static final Logger LOGGER = Logger.getLogger(AbstractBuild.class.getName());
  998. }