Rev 7494 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.erggroup.buildtool.ripple;import java.sql.SQLException;import java.util.ArrayList;import java.util.Collections;import java.util.Iterator;import java.util.List;import java.util.ListIterator;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.erggroup.buildtool.ripple.BuildFile.BuildFileState;import com.erggroup.buildtool.ripple.Package.PkgDependency;import com.erggroup.buildtool.ripple.ReleaseManager.BuildReason;import com.erggroup.buildtool.smtp.CreateUrls;import com.erggroup.buildtool.smtp.Smtpsend;import com.erggroup.buildtool.utilities.StringAppender;import com.erggroup.buildtool.utilities.XmlBuilder;/**Plans release impact by generating a set of Strings containing build file content.*/public class RippleEngine{/**configured mail server* @attribute*/private String mMailServer = "";/**configured mail sender user* @attribute*/private String mMailSender = "";/**configured global email target* @attribute*/private String mMailGlobalTarget = "";/** Vector of email addresses for global and project wide use* Most emails will be sent to this list of email recipients*/public List<String> mMailGlobalCollection = new ArrayList<String>();/**name associated with the baseline* @attribute*/public String mBaselineName = "";/** Data to control the build plan**/public PlanControl mPlanControl = new PlanControl();/**Collection of build exceptions associated with the baseline* Used to determine (and report) what change in build exceptions happens as part of planRelease* Daemon centric* @aggregation shared* @attribute*/ArrayList<BuildExclusion> mBuildExclusionCollection = new ArrayList<BuildExclusion>();/**Logger* @attribute*/private static final Logger mLogger = LoggerFactory.getLogger(RippleEngine.class);/** Escrow information - commands to set up escrow* @attribute*/private String mEscrowSetup;/** Escrow information - Raw data (May not be used)* @attribute*/private String mEscrowRawData;/** Collections of packages*/private PackageCollection mPackageCollection = new PackageCollection();private PackageCollection mPackageCollectionWip = new PackageCollection();private PackageCollection mPackageCollectionTest = new PackageCollection();private PackageCollection mPackageCollectionRipple = new PackageCollection();private PackageCollection mPackageCollectionAll = new PackageCollection();/**index to current String item* @attribute*/private int mBuildIndex;/**Database abstraction* @attribute*/ReleaseManager mReleaseManager;/**Baseline identifier (rtag_id for a release manager baseline, bom_id for deployment manager baseline)* @attribute*/private int mBaseline;/** Escrow Only: SBOM_ID*/private int mSbomId;/** RTAG_ID* Set from mBaseline*/private int mRtagId;/**When true, mBuildCollection contains one item based on a release manager rtag_id and contains a daemon property* When false, mBuildCollection contains at least one item based on a deployment manager bom_id* Will be accessed by the Package class to calculate its mAlias* @attribute*/public boolean mDaemon;/**collection of build file content in String form* @attribute*/private ArrayList<BuildFile> mBuildCollection = new ArrayList<BuildFile>();/** List of packages that we plan to build* Used to provide feedback into RM* Only the first entry is about to be built as we re-plan every cycle*/private ArrayList<PlannedPackage> mBuildOrder = new ArrayList <PlannedPackage>();/**Warning message* @attribute*/private static final String mAnyBuildPlatforms = "Warning. The following package versions are not reproducible on any build platform: ";/**Flag to control output to standard out* @attribute*/private boolean mAnyBuildPlatformsFlag = true;/**Warning message* @attribute*/private static final String mAssocBuildPlatforms = "Warning. The following package versions are not reproducible on the build platforms associated with this baseline: ";/**Flag to control output to standard out* @attribute*/private boolean mAssocBuildPlatformsFlag = true;/**Warning message* @attribute*/private static final String mNotInBaseline = "Warning. The following package versions are not reproducible as they are directly dependent upon package versions not in the baseline: ";/**Flag to control output to standard out* @attribute*/private boolean mNotInBaselineFlag = true;/**Warning message* @attribute*/private static final String mDependent = "Warning. The following package versions are not reproducible as they are directly/indirectly dependent upon not reproducible package versions: ";/**Flag to control output to standard out* @attribute*/private boolean mDependentFlag = true;/**Warning message* @attribute*/private static final String mCircularDependency = "Warning. The following package versions are not reproducible as they have circular dependencies: ";/**Flag to control output to standard out* @attribute*/private boolean mCircularDependencyFlag = true;/** String used to terminate lines* @attribute*/private static final String mlf = System.getProperty("line.separator");/** XML File Prefix*/private static final String mXmlHeader = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>" + mlf;/**RippleEngine constructor* @param releaseManager - Associated releaseManager instance* @param rtagId - Release Identifier* @param isDaemon - Mode of operation. False: Escrow, True: Daemon*/public RippleEngine(ReleaseManager releaseManager, int rtagId, boolean isDaemon){mLogger.debug("RippleEngine rtag_id {} isDaemon {}", rtagId, isDaemon);mReleaseManager = releaseManager;mBaseline = rtagId;mRtagId = rtagId;mDaemon = isDaemon;mReleaseManager.setDaemonMode(mDaemon);}/*** getRtagId* @return The rtagId of the Release attached to this instance of the RippleEngine*/public int getRtagId(){return mRtagId;}/**Plan what is to be built* <br>Discards all build file content* <br>Generates new build file content** @param lastBuildActive - False. Daemon Mode. The last build was a dummy.* This planning session may not result in a build* - True. Daemon Mode. The last build was not a dummy.* There is a very good chance that this planning session* will result in a build, so it is given priority to connect* to the database.*/public void planRelease(final boolean lastBuildActive) throws SQLException, Exception{mLogger.warn("planRelease mDaemon {}", mDaemon);//// Diagnostic output// Having issues with memory usage//long totalMem = Runtime.getRuntime().totalMemory();long freeMem = Runtime.getRuntime().freeMemory();mLogger.warn("Memory Usage: Total: {}, Free: {}, Used: {}", totalMem, freeMem, totalMem - freeMem);mBuildCollection.clear();mPackageCollection.clear();mPackageCollectionRipple.clear();mPackageCollectionTest.clear();mPackageCollectionWip.clear();mPackageCollectionAll.clear();mBuildOrder.clear();mEscrowRawData = "";mEscrowSetup = "";Phase phase = new Phase("Plan");// use finally block in planRelease to ensure the connection is releasedtry{phase.setPhase("connectForPlanning");mReleaseManager.connectForPlanning(lastBuildActive);if ( mDaemon ){// claim the mutexmLogger.warn("planRelease claimMutex");phase.setPhase("claimMutex");mReleaseManager.claimMutex();// Populate the mBuildExclusionCollection//// Builds are either 'Directly excluded' or 'Indirectly excluded'// Direct excludes result from:// User request// Build failure// Inability to build package// Indirectly excluded packages result from have a build dependency on a package// that is directly excluded.//// In the following code we will extract from the Database all build exclusions// We will then add 'Relevant' entries to mBuildExclusionCollection// and delete, from the database those that are no longer relevant - ie indirectly excluded// items where the root cause is no longer in the set.phase.setPhase("mBuildExclusionCollection");mBuildExclusionCollection.clear();ArrayList<BuildExclusion> tempBuildExclusionCollection = new ArrayList<BuildExclusion>();mLogger.debug("planRelease queryBuildExclusions");mReleaseManager.queryBuildExclusions(tempBuildExclusionCollection, mBaseline);// only populate mBuildExclusionCollection with tempBuildExclusionCollection entries which have a relevant root_pv_id// The entry is relevant if:// It is for a directly excluded package,other than a RippleStop// It is for an indirectly excluded package AND the reason for exclusion still exists//for (Iterator<BuildExclusion> it = tempBuildExclusionCollection.iterator(); it.hasNext(); ){BuildExclusion buildExclusion = it.next();if ( buildExclusion.isRelevant(tempBuildExclusionCollection) ){mBuildExclusionCollection.add(buildExclusion);}else{// Remove the indirectly excluded entry as its root cause// is no longer present.buildExclusion.includeToBuild(mReleaseManager, mBaseline);}}}//-----------------------------------------------------------------------// Query package versions//phase.setPhase("queryPackageVersions");mLogger.debug("planRelease queryPackageVersions");mReleaseManager.queryPackageVersions(this, mPackageCollection, mBaseline);phase.setPhase("queryPackageWips");mReleaseManager.queryWips(this, mPackageCollectionWip, mBaseline);mReleaseManager.queryTest(this, mPackageCollectionTest, mBaseline);mReleaseManager.queryRipples(this, mPackageCollectionRipple, mBaseline);mPackageCollectionAll.addAll(mPackageCollection);mPackageCollectionAll.addAll(mPackageCollectionWip);mPackageCollectionAll.addAll(mPackageCollectionTest);mPackageCollectionAll.addAll(mPackageCollectionRipple);// Sort the collection by PVID// Unit Test output order is known// May assist in creating repeatable build orders//Collections.sort(mPackageCollectionAll.mCollection, Package.SeqComparator);//------------------------------------------------------------------------// Process packages collected// Determine and tag those we can't buildphase.setPhase("processPackages");processPackages();//-----------------------------------------------------------------------// At this point we have tagged all the packages that we cannot build// Now we can determine what we are buildingphase.setPhase("planBuildOrder");mLogger.debug("planRelease process Remaining");planBuildOrder();// Report excluded packages and the build plan// This is being done with the MUTEX being held, but the// trade off is the cost of getting a connection.//if ( mDaemon ){phase.setPhase("Report Change");reportChange();phase.setPhase("Report Plan");reportPlan();}//// Generate the build Files//phase.setPhase("generateBuildFiles");generateBuildFiles();}finally{mLogger.debug("planRelease finally");// this block is executed regardless of what happens in the try block// even if an exception is thrown// ensure the SELECT FOR UPDATE is releasedtry{if ( mDaemon ){// attempt to release the SELECT FOR UPDATE through a commit// a commit must be done in the normal case// a commit may as well be done in the Exception case// in the case of a SQLException indicating database connectivity has been lost// having a go at the commit is superfluous// as the SELECT FOR UPDATE will have been released upon disconnectionphase.setPhase("releaseMutex");mReleaseManager.releaseMutex();}}finally{// ensure disconnect under all error conditionsphase.setPhase("disconnectForPlanning");mReleaseManager.disconnectForPlanning(lastBuildActive);}}//// Attempt to release objects that have been created during the planning phase//mPackageCollection.clear();mPackageCollectionRipple.clear();mPackageCollectionTest.clear();mPackageCollectionWip.clear();mPackageCollectionAll.clear();mBuildOrder.clear();mLogger.warn("planRelease mDaemon {} returned", mDaemon);phase.setPhase("EndPlan");}/** Process packages that have been collected as a part of the plan* @param phase* @throws SQLException* @throws Exception*/private void processPackages() throws SQLException, Exception {// Deal with test builds here as they may impact upon package attributes// eg: dependency collection and build standard differences// Note: Done before mPackageDependencyCollection is setup//Phase phase = new Phase("processPackages");if ( mDaemon ){// Exclude packages that have been specifically excluded because// They have failed to build// User says so// Only examine the ReleaseCollection, Wip and Ripple Collections// Can testBuild a package that has been excluded//phase.setPhase("Exclude packages");for (ListIterator<BuildExclusion> it = mBuildExclusionCollection.listIterator(); it.hasNext(); ){BuildExclusion be = it.next();mLogger.debug("BE: {}", be);if ( be.isARootCause() ){Package rootPackage = mPackageCollectionAll.contains(be.mId);if (rootPackage != null) {Package p;p = mPackageCollection.contains(rootPackage.mAlias);if (p != null) {p.excludeFromBuilds(-3, "Exclude from Release {}");}p = mPackageCollectionWip.contains(be.mId);if (p != null) {p.excludeFromBuilds(-3, "Exclude from WIP {}");}p = mPackageCollectionTest.contains(be.mId);if (p != null) {p.excludeFromBuilds(-3, "Exclude from Test {}");}}}}// Process test builds - they are in their own collectionphase.setPhase("TestBuilds");for (Iterator<Package> it = mPackageCollectionTest.iterator(); it.hasNext(); ){Package p = it.next();mLogger.debug("planRelease package test build {}", p.mAlias);//// Cannot test build an SDK based package or a Pegged Package// Remove the test build request from the database// Send a warning email//if(p.mIsPegged || p.mIsSdk){String reason;reason = (p.mIsPegged) ? "Pegged" : "SDK Based";mLogger.warn("planRelease Daemon Instruction (testBuild) of {} package deleted: {}", reason, p.mAlias);mReleaseManager.markDaemonInstCompleted( p.mTestBuildInstruction );emailRejectedDaemonInstruction("Cannot 'Test Build' a " + reason + " package",p);}// force patch for test build numberingp.mDirectlyPlanned = true;p.mChangeType.setPatch();p.mRequiresSourceControlInteraction = false;p.mBuildReason = BuildReason.Test;p.mIndirectlyPlanned = true;}}// Set up mPackageDependencyCollection on each package// Examine the dependencies by alias and convert this to a 'package' selected from the released package setphase.setPhase("setPackageDependencyCollection");mLogger.debug("planRelease setup setPackageDependencyCollection");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();for (Iterator<PkgDependency> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); ){PkgDependency depEntry = it2.next();Package dependency = mPackageCollection.contains(depEntry.alias);if (dependency != null)depEntry.pkg = dependency;}}// Detect and deal with circular dependencies// Examine all packages in the Release. Other packages will be examined laterphase.setPhase("Detect Circular Dependencies");mLogger.debug("planRelease deal with circular dependencies");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.hasCircularDependency( mPackageCollection ) ){mLogger.info("planRelease circular dependency detected {}", p.mAlias);// Force this package to be marked as having a circular dependency - even if its been excludedp.mBuildFile = 0;// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, "Package has circular dependency", -6);// take the package out of the buildstandardOut(mCircularDependency, p.mAlias, mCircularDependencyFlag);mCircularDependencyFlag = false;}}// Scan for packages with missing dependencies// ie: The dependent package is not in the package Collectionphase.setPhase("Scan missing dpendencies");mLogger.debug("planRelease use the fully built mPackageDependencyCollection");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( mDaemon ){// Daemon Mode Only// Not interested in the dependencies of a pegged package or a package provided from an SDK.// Such packages will be deemed to not have missing dependenciesif (p.mIsPegged || p.mIsSdk){continue;}}for (Iterator<PkgDependency> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); ){PkgDependency depEntry = it2.next();Package dependency = mPackageCollection.contains(depEntry.alias);if (dependency == null){mLogger.info("planRelease dependency is not in the baseline {}", depEntry);// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, "Package build dependency not in the release", -4);// take the package out of the buildstandardOut(mNotInBaseline, p.mAlias, mNotInBaselineFlag);mNotInBaselineFlag = false;break;}}}// Detect packages with no build standard and exclude them from the buildphase.setPhase("Scan not reproducible");mLogger.debug("planRelease process packages which are not reproducible");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile == 0){if ( mDaemon ){// Daemon Mode Only// Not interested in the reproducibility of a pegged package or a package provided from an SDK.// Such packages will be deemed to be reproducible - but out side of this releaseif (p.mIsPegged || p.mIsSdk){continue;}}// Does the package have a build standard. If not then we can't reproduce it.// Escrow - Assume the package is provided// Daemon - Exclude this package, but not its consumers. If the package is available then we can use it.if (!p.isReproducible()){mLogger.info("planRelease package not reproducible {}" ,p.mName);// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, "Package has no build environment", -1);// package is not reproducible, discardstandardOut(mAnyBuildPlatforms, p.mAlias, mAnyBuildPlatformsFlag);mAnyBuildPlatformsFlag = false;}}}// Process packages which are not reproducible on the configured set of build machines.//// Test each package and determine if the package contains a buildStandard that// can be processed by one of the machines in the build set//// ie: Package: Win32:Production and we have a BM with a class of 'Win32'//// Only exclude the failing package and not its dependents// May be legitimate in the release//phase.setPhase("Scan not reproducible2");mLogger.debug("planRelease process packages which are not reproducible2");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile == 0){if ( mDaemon ){// Daemon Mode Only// Not interested in the reproducibility of a pegged package or a package provided from an SDK.// Such packages will be deemed to be reproducible - but out side of this releaseif (p.mIsPegged || p.mIsSdk){continue;}}// package has yet to be processed// assume it does not need to be reproduced for this baseline//// For each machineClass in the buildsetboolean reproduce = false;for (Iterator<String> it2 = mReleaseManager.mReleaseConfigCollection.mMachineClasses.iterator(); it2.hasNext(); ){String machineClass = it2.next();if ( p.canBeBuildby(machineClass)){reproduce = true;mLogger.info("planRelease package built on {} {}", machineClass, p.mAlias );break;}}if ( !reproduce ){mLogger.info("planRelease package not reproducible on the build platforms configured for this baseline {}", p.mName);// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, "Package not built for configured platforms", -2);// package is not reproducible on the build platforms configured for this baseline, discardstandardOut(mAssocBuildPlatforms, p.mAlias, mAssocBuildPlatformsFlag);mAssocBuildPlatformsFlag = false;}}}if (mDaemon ){// Daemon Mode Only// Process packages which need to be ripple builtphase.setPhase("Scan for ripples");mLogger.debug("Process packages which need to be ripple built");for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile == 0){// Not interested in a pegged package or a package provided from an SDK.// Such packages are not rippledif (p.mIsPegged || p.mIsSdk){continue;}// Examine this packages dependencies// If one of them does not exist in the 'official release' set then// the package needs to be built against the official release set// and we can't build any of its dependent packagesIterator<PkgDependency> it2 = p.mDependencyCollection.iterator();while ( it2.hasNext() ){PkgDependency depEntry = it2.next();if ( !depEntry.pkg.mAdvisoryRipple ){// not advisory, ie: has ripple build impactif ( !isInRelease(depEntry.pvId) ){// the package is out of date// exclude all dependent package versionsmLogger.info("planRelease package out of date {}", p.mName);p.mBuildReason = BuildReason.Ripple;rippleIndirectlyPlanned(p);// This package needs to be rippled// If this package has a rippleStop marker of 's', then we cannot// build this package at the moment.// Packages that depend on this package have been excluded// We need to exclude this one too//if (p.mRippleStop == 's' || p.mRippleStop == 'w'){// Package marked as a rippleStop// max 50 charsrippleBuildExclude(p, -2, "Ripple Required." + " Waiting for user", -11);if (p.mRippleStop == 's' ) {// Need to flag to users that the package build is waiting user actionmLogger.info("planRelease Ripple Required. Stopped by flag {}", p.mName);mReleaseManager.setRippleStopWait(mRtagId,p);emailRippleStop(p);}}break;}}}}}// Daemon Mode Only// Process packages which do not exist in the archive// For unit test purposes, assume all packages exist in the archive if releasedmLogger.debug("planRelease process packages which do not exist in the archive");phase.setPhase("Scan dpkg_archive");if ( mReleaseManager.mUseDatabase ){for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile == 0){// package has yet to be processedif (!p.mDirectlyPlanned && !p.mIndirectlyPlanned && p.mForcedRippleInstruction == 0){// check package version archive existenceif (!p.existsInDpkgArchive()){if (! p.mIsBuildable){// Package does not exist in dpkg_archive and it has been flagged as unbuildable// This may be because is Unbuildable or Manually builtmLogger.info("planRelease Unbuildable package not found in archive {}", p.mName);// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, "Unbuildable" + " package not found in archive", -10);}// Not interested in a pegged package or a package provided from an SDK.// Such packages are not rippledelse if (p.mIsPegged || p.mIsSdk){String reason;reason = (p.mIsPegged) ? "Pegged" : "SDK";// Pegged packages or packages provided from an SDK MUST exist in dpkg_archive// They will not be built within the context of this release. It is the responsibility// of another release to build them.mLogger.info("planRelease {} package not found in archive {}", reason, p.mName);// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, reason + " package not found in archive", -7);}else if (p.mForcedRippleInstruction == 0){// [JATS-331] Unable to rebuild package with Advisory Ripple dependencies// Examine this packages dependencies// If one of them does not exist in the 'official release' set then// the package cannot be rebuilt in this release.for (Iterator<PkgDependency> it2 = p.mDependencyCollection.iterator() ; it2.hasNext() ;){PkgDependency dpvId = it2.next();if ( !isInRelease(dpvId.pvId) ){// This package cannot be rebuilt as one of its dependents is NOT in this release// exclude all dependent package versionsmLogger.info("planRelease package not found in archive. Cannot be rebuilt due to {}", p.mName);// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, "Package cannot be rebuilt in this release", -4);break;}}// The package has not been excluded from the buildif (p.mBuildFile == 0){mLogger.info("planRelease package not found in archive {}", p.mName);// DEVI 47395 the cause of this build is not WIP or ripple induced,// it simply does not exist in the archive (has been removed)// prevent source control interactionp.mRequiresSourceControlInteraction = false;p.mBuildReason = BuildReason.Restore;rippleIndirectlyPlanned(p);}}}}}}}// Daemon Mode Only// Detect bad forced ripples requests and reject themmLogger.debug("planRelease process forced ripples");phase.setPhase("Test bad ripple requests");for (Iterator<Package> it = mPackageCollectionRipple.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile == 0){//// Cannot force a ripple on an SDK based package or a Pegged Package// Remove the daemon instruction from the database// Send a warning email//if(p.mIsPegged || p.mIsSdk){String reason;reason = (p.mIsPegged) ? "Pegged" : "SDK Based";mLogger.warn("planRelease Daemon Instruction of {} package deleted: {}", reason, p.mName);mReleaseManager.markDaemonInstCompleted( p.mForcedRippleInstruction );emailRejectedDaemonInstruction("Cannot 'Ripple' a " + reason + " package",p);p.mBuildFile = -8;}}}// Daemon Mode Only// Mark Pegged and SDK packages as not to be built// Mark RippleStoped packages as not to be build// Have previously detected conflicts between pegged/sdk packages and daemon instructions//mLogger.debug("planRelease remove pegged and SDK packages from the build set");phase.setPhase("Remove Pegged and SDKs");for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile == 0){String reason = null;// Not interested in a pegged package or a package provided from an SDK or packages with an active RippleStop// Such packages are not builtif (p.mIsPegged ) {reason = "Pegged";p.mBuildFile = -8;} else if (p.mIsSdk) {reason = "SDK";p.mBuildFile = -8;} else if (p.mRippleStop == 's' || p.mRippleStop == 'w') {reason = "RippleStop";p.mBuildFile = -11;}if (reason != null){mLogger.info("planRelease {} not built in this release {}", reason, p.mName);}}}}else{// escrow reporting only// Report packages that are not reproducible//// Note: I don't believe this code can be executed// The -3 is only set in daemon modefor (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile == -3){standardOut(mDependent, p.mAlias, mDependentFlag);mDependentFlag = false;}}}phase.setPhase("End");}/** Plan the build order.* Assumes that a great deal of work has been done.* This is a stand alone method to contain the work** @throws Exception* @throws SQLException*/private void planBuildOrder() throws Exception, SQLException{/*** Current status* basicPlan - time to complete current ripple* FullPlan - Include all WIPS/RIPPLES into the release set** If the full Plan does NOT extend the time of the basicPLan by 20% then use the full plan* otherwise complete the current ripple and the include WIPs/RIPPLEs\** TEST requests will be done first.**/// Process remaining packages which are need to be reproduced for this baseline.// Determine the build file for each package// For daemon builds:// Determine the next package that can be built now// Sounds simple - doesn't it// Set its mBuildNumber to 1, all remaining reproducible packages to 2// For escrow builds:// Determine the package versions that can be built in the build iteration// Set their mBuildNumber to the build iteration// Increment the build iteration and repeat until all package versions// that need to be reproduced have been assigned a build iteration//// Generate a plan for the packages that are in the current release// These may be the result of a ripple going through the system// or a rebuild of a missing package or the result of a merge.//// This will provide our basic plan, before we consider adding new packages to the// mix, such as those from user WIPS and RIPPLE requests.//PlanResults basicPlan = planCollection("Basic", mPackageCollection, true);addWipsAndRipples (basicPlan);// Have not considered any of the WIP or RIPPLE packages// If any of them are in the basic plan, then we can ignore them as we will build them// when we build the basic plan//// Determine a set of packages that will not be included if we build the basic plan//// At the moment - lets assume there are none// What do we do ?// Select the first request// Replace the package in the released collection// Run the plan algorithm// Restore the release collection//if ( mDaemon ){//// Need to consider the TEST requests// - Valid requests will be placed first on the build order//mBuildOrder.clear();for (Iterator<Package> it = mPackageCollectionTest.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile >= 0){mBuildOrder.add( new PlannedPackage(p) );}}//// Examine Plan control// DropPlan - do not use the basic plan. This is a one-shot request// Threshold == 0. Always use a full planif (mPlanControl.mDumpPlan || mPlanControl.mThreshold == 0){mLogger.warn("Drop basicPlan and use fullPlan");basicPlan.planTime = 0;basicPlan.planCollection.clear();// Always reset the one-shot request to drop the current planmReleaseManager.resetPlanControl(mBaseline);}// First attempt// Insert all the WIPs and RIPPLES into the buildset.// Generate a plan and see how much the time is extended.//// Don't modify mPackageCollection, although data in the 'Package' in the underlying packages may changePlanResults fullPlan = postRipplePlan("Full", basicPlan, true);// Decide with plan to use// At the moment we have, at most two.// - (basic) Current Release ripples// - (full) Current Release + All WIPS and RIPPLES//// If we have both then use the planContol threshold to determine which plan to use// The threshold number is taken as a percent by which the fullPlan Time impacts basicPlan time// before the fullPlan will be rejected.// ie: threshold = 20. If fullPlan is 20% longer that the basicPlan, then use the basicPlan then a modified fullPlan (Cautious)// ie: threshold = 100. If fullPlan is 100% longer that the basicPlan, then use the basicPlan then a modified fullPlan (Aggressive)//// A modified fullPlan is one without the natural ripples as these will have been done in the basicPlan//// Add the planned packages into the buildOrderif( !basicPlan.planCollection.isEmpty() && !fullPlan.planCollection.isEmpty() ){// Calculate the build impact as a percentage// The impact of the fullPlan over the basicPlan//int buildImpact = 0;if (fullPlan.planTime != basicPlan.planTime && basicPlan.planTime != 0) {buildImpact = ((fullPlan.planTime - basicPlan.planTime) * 100) / (basicPlan.planTime);}mLogger.warn("Two plan selection: Basic:{} Full:{}, Impact:{}% Threshold:{}% Exceeded: {}", basicPlan.planTime,fullPlan.planTime, buildImpact, mPlanControl.mThreshold, buildImpact >= mPlanControl.mThreshold);if ( buildImpact >= mPlanControl.mThreshold ){// BuildImpact too high// Use the basic plan FOLLOWED by a plan that does not include// Ripples done by the basic plan// WIPs/RIPPLES done in the course of doing the basic planmLogger.warn("Use basic plan, then modified full plan");mBuildOrder.addAll(basicPlan.planCollection);fullPlan = postRipplePlan("Full-Ripples", basicPlan, false);// Insert a marker package to indicate a 'newPlan'PlannedPackage pp = new PlannedPackage( new Package() );pp.mBuildLevel = -1;mBuildOrder.add(pp);mBuildOrder.addAll(fullPlan.planCollection);} else {// Use the full planmLogger.warn("Use full plan");mBuildOrder.addAll(fullPlan.planCollection);}} else if (!basicPlan.planCollection.isEmpty() ) {// Use the basic planmLogger.warn("Use basic plan");mBuildOrder.addAll(basicPlan.planCollection);} else if ( !fullPlan.planCollection.isEmpty() ) {// Use the full planmLogger.warn("Use full plan");mBuildOrder.addAll(fullPlan.planCollection);} else {// Do not have a plan// May have tests requestsmLogger.warn("Use NO plan");}//// Now have a build order// Allocate new version numbers// Examine packages with a buidLevel of zero// If we fail all of them then we can't build anything//mLogger.warn("Determine new version numbers");Package build = ReleaseManager.NULL_PACKAGE;for (Iterator<PlannedPackage> it = mBuildOrder.iterator(); it.hasNext(); ){PlannedPackage pkg = it.next();if (pkg.mBuildLevel != 0){continue;}//// Attempt to allocate a new version number// If we can't generate a new version number, then this is considered to be a build failure// The package will be excluded and the user will be emailed//Package p = pkg.mPkg;int pvApplied = p.applyPV(mReleaseManager);if ( pvApplied == 0){build = p;break;}else if ( pvApplied == 1 ){// max 50 charsrippleBuildExclude(p, p.mId, "Package has non standard versioning", -12);}else if ( pvApplied == 2 ){// max 50 charsrippleBuildExclude(p, p.mId, "Package has reached ripple field limitations", -12);}else if ( pvApplied == 3 ){// max 50 charsrippleBuildExclude(p, p.mId, "Package has invalid change type", -12);}else{// max 50 chars// Bad programming - should not get hererippleBuildExclude(p, p.mId, "Unable to calculate next version", -12);}}// Now have an mBuildOrder// Mark the selected package in the build order as the one to be built// May need to process its it a bit more//if ( build != ReleaseManager.NULL_PACKAGE){build.mBuildFile = 1;if ( build.mForcedRippleInstruction > 0 ){mReleaseManager.markDaemonInstCompleted( build.mForcedRippleInstruction );}if ( build.mTestBuildInstruction > 0 ){mReleaseManager.markDaemonInstInProgress( build.mTestBuildInstruction );}// Now that we know which package we are building// Set the previously calculated nextVersion as the packages version number// Claim the version number to prevent other builds from using it. Even if doing a test build//mLogger.debug("Update mVersion: {}", build);if (build.mNextVersion != null){mReleaseManager.claimVersion(build.mPid, build.mNextVersion + build.mExtension, mBaseline);build.mVersion = build.mNextVersion;mLogger.warn("Update mVersion: {} to {}", build, build.mVersion);}//// Ensure that the package we are about to build is present in the mPackageCollection as its// this list that is used to generate the build.xml file - that may be a bad way to do it , but ...//// Note: Can't really modify others as they will be used as dependencies//mPackageCollection.upsert(build);build.mIsNotReleased = false;}//// Report packages that are indirectly excluded// ie: They will not be built because one, or more, of their dependents is not buildable//mLogger.warn("planBuildOrder process packages which are not ripple buildable");Package.resetCircularDependency (mPackageCollection);for (ListIterator<BuildExclusion> it = mBuildExclusionCollection.listIterator(); it.hasNext(); ){BuildExclusion be = it.next();// Only process direct exclusions// mBuildExclusionCollection is at this point based on relevant (direct and indirect)// excluded pv's in the database//if ( be.isARootCause() ){for (Iterator<Package> it1 = mPackageCollectionAll.iterator(); it1.hasNext(); ){Package p = it1.next();// TODO - Do we need && p.mTestBuildInstruction == 0 && p.mForcedRippleInstruction == 0if ( be.compare(p.mId) ){// package is not reproducible, discard it and its consumers//mLogger.warn("Excluded Package {}, {}", p, be );ArrayList<Package> toExclude = new ArrayList<Package>();toExclude.addAll(usedByAnyPackages(p, mPackageCollection ));// Process packages that we need to exclude indirectly//while ( ! toExclude.isEmpty()){Package pkg = toExclude.remove(0);// If this package has not been excluded (for whatever reason), than add itboolean found = false;for (Iterator<BuildExclusion> it2 = mBuildExclusionCollection.iterator(); it2.hasNext(); ){BuildExclusion buildExclusion = it2.next();if (buildExclusion.compare(pkg.mId) ){found = true;if ( buildExclusion.isImported() ){// An exclusion for this package already exists// If it was 'imported' from the database then// mark it as processed so that it will be persisted//// Otherwise it will be a new one//buildExclusion.setProcessed();}break;}}if (!found){BuildExclusion buildExclusion = new BuildExclusion(pkg.mId, p.mId, null, p.mTestBuildInstruction);it.add(buildExclusion);mLogger.warn("Indirectly Excluded Package {}", pkg.mAlias);pkg.mBuildFile = -5;}// Determine all packages that use this excluded package// THAT WE HAVE NOT ALREADY PROCESSED - circular dependencies are a killerArrayList<Package> usedBy = usedByAnyPackages(pkg, mPackageCollection );for (Iterator<Package> it2 = usedBy.iterator(); it2.hasNext(); ){Package uPkg = it2.next();if (!uPkg.mCheckedCircularDependency){toExclude.add(uPkg);uPkg.mCheckedCircularDependency = true;}}}}}}}//// Handle daemon Instructions that cannot be satisfied// Need to be rejected to prevent continuous planning//mLogger.warn("Handle unsatisfied Daemon Instructions");reportUnsatisfiedDaemonInstructions();//// Examine the build order and 'delete' entries with a -ve mBuildFile// These will be the product of pvApply errors and the packages that depend on them//for (Iterator<PlannedPackage> it = mBuildOrder.listIterator(); it.hasNext(); ){PlannedPackage pkg = it.next();if (pkg.mPkg.mBuildFile < 0){it.remove();mLogger.warn("Purge mBuildOrder {}", pkg.mPkg);continue;}}//// If the first entry is a 'new Plan' marker, then remove it// It will be the result of applyPvs that failedif ( !mBuildOrder.isEmpty() ){PlannedPackage pkg = mBuildOrder.get(0);if (pkg.mBuildLevel < 0)mBuildOrder.remove(0);}//// To fit in with the old algorithm ( ie: could be improved )// Insert marks into all packages// Not sure exactly why - Its used in the generation of the ant build file// Want to set mNoBuildReason, mBuildFile//// Package we have selected to build: 0 , 1// Package we could have built : 0 , 2// Packages we can't build : reason, 3// Packages that are OK : 3 , 3// ???? : 0 , 3for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if (p == build ) {p.mNoBuildReason = 0;p.mBuildFile = 1;}else if ( p.mBuildFile < 0 ){p.mNoBuildReason = p.mBuildFile;p.mBuildFile = 3;}else if (p.mBuildReason != null){p.mNoBuildReason = 0;p.mBuildFile = 2;}else{p.mNoBuildReason = 0;p.mBuildFile = 3;}}}else{// Escrow// The basic plan is the escrow build ordermBuildOrder = basicPlan.planCollection;for (Iterator<PlannedPackage> it = mBuildOrder.iterator(); it.hasNext(); ){PlannedPackage p = it.next();p.mPkg.mNoBuildReason = p.mPkg.mBuildFile;p.mPkg.mBuildFile = p.mBuildLevel + 1;}}mLogger.warn("Final Plan");for (Iterator<PlannedPackage> it = mBuildOrder.iterator(); it.hasNext(); ){PlannedPackage p = it.next();mLogger.warn("Plan: {} {} {}", p.mBuildLevel, p.mPkg.mAlias, p.mPkg.mId);}}/*** Post process the basic plan to replace packages with WIPs and RIPPLES* Done so that the displayed plan correctly indicates the time that these will be processed** @param basicPlan*/private void addWipsAndRipples(PlanResults basicPlan) {for (Iterator<PlannedPackage> it = basicPlan.planCollection.iterator(); it.hasNext(); ){PlannedPackage pkg = it.next();Package p = mPackageCollectionWip.contains(pkg.mPkg.mAlias);if (p == null ) {p = mPackageCollectionRipple.contains(pkg.mPkg.mAlias);}// Have a WIP/RIPPLE will be processed in the basic plan// If its eligible to be built then replace the package entry in the planif (p != null && p.mBuildFile >= 0 ) {mLogger.warn("WIP/RIPPLE consumed into basic plan: {}", p);pkg.mPkg = p;}}}/** Internal class to contain intermediate results*/class PlanResults {int planTime = 0;ArrayList<PlannedPackage> planCollection = new ArrayList<PlannedPackage>();}/*** Process a collection of packages and generate a collection of package plans* A package plan is a collection of packages that can be built* The first item in the list is a package that can be built right now* Other items in the list will be packages that can be built now or will need to be built as the result* of a ripple** The method will be called multiple times so that we can evaluate different plans* The underling packages will have data and flags that will need to be rested before a calculation** @param name - Name of the plan* @param packageCollection - Base collection of packages to plan* @param mode - true: Include 'Ripples'** @return*/private PlanResults planCollection(String name, PackageCollection packageCollection, boolean mode){Phase phase = new Phase("Plan-" + name);ArrayList<PlannedPackage> ripplePlan = new ArrayList<PlannedPackage>();PlanResults results = new PlanResults();// Reset flags used in the calculationsPackage.resetProcessed(packageCollection);// Exclude all packages that cannot be built and all packages that depend on them// First find packages to be directly excludedArrayList<Package>exclude = new ArrayList<Package>();phase.setPhase("Exclude Unbuildable");for (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile == -8 ) {// Mark SDK or Pegged so that we don't build them, but can build their dependentsp.mProcessed = true;p.mIsProcessed = true;mLogger.warn("SDK/Peg Exclude: {}", p);} else if (p.mBuildFile < 0 ) {exclude.add(p);mLogger.warn("Direct Exclude: {}", p);}}// Exclude packages that have failed to be built// These are recorded in mBuildExclusionCollectionphase.setPhase("Exclude Build Failures");for (ListIterator<BuildExclusion> it = mBuildExclusionCollection.listIterator(); it.hasNext(); ){BuildExclusion be = it.next();mLogger.warn("BE: {}", be);if ( !be.isAIndirectError() ){for (Iterator<Package> it1 = packageCollection.iterator(); it1.hasNext(); ){Package p = it1.next();if (p.mBuildFile >= 0 && p.mId == be.mId) {p.mBuildFile = -3;mLogger.warn("AddExclude {}",p);exclude.add(p);break;}}}}// Process the list of packages to be excluded// Add to the list packages that depend on the excluded package that have not already been excludedphase.setPhase("ExcludeAllUsed");while( !exclude.isEmpty() ){Package p = exclude.remove(0);mLogger.info("planCollection package not buildable {}. {},{}", p, p.mProcessed, p.mIsProcessed);p.mProcessed = true; // Used to indicate pkg has had its dependencies scannedp.mIsProcessed = true; // Used to indicate pkg has been added to list to processfor (Iterator<Package> it1 = packageCollection.iterator(); it1.hasNext(); ){Package pkg = it1.next();if (pkg.mProcessed) {continue;}for (Iterator<PkgDependency> it = pkg.mDependencyCollection.iterator(); it.hasNext(); ){PkgDependency depEntry = it.next();if ( p.mAlias.compareTo( depEntry.alias ) == 0 ) {if (!pkg.mIsProcessed) {pkg.mIsProcessed = true;exclude.add(pkg);}}}}}// Have a collection packages that we can build right now// Create the ripplePlanphase.setPhase("Calc Build Order");if (mDaemon){ArrayList<PlannedPackage> toBuild = new ArrayList<PlannedPackage>();//// Daemon Mode// Generate the ripplePlan - collection of packages that we need to build in the current ripple//// Determine if we have a reason to build anything in this collection of buildable packages// Reset mProcessed - it will be used to detect that we have processed a packagefor (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); ){Package p = it.next();if (!p.mIsProcessed) {PlannedPackage pPkg = new PlannedPackage(p);results.planCollection.add(pPkg);pPkg.mProcessed = false;if (p.mBuildReason != null) {if (mode || ( !mode && p.mBuildReason != BuildReason.Ripple )) {toBuild.add(pPkg);}}}}// Need (would like to) build stuff// Determine the packages that we need to build and the time that it will take//if ( !toBuild.isEmpty() ){// Locate the packages that we need to build// The build order cannot be correctly determined at this time// Need to add elements to the end of the list while processing// Sum the buildTimes of the packages that we add to the listwhile ( ! toBuild.isEmpty()){PlannedPackage pkg = toBuild.remove(0);if ( ! pkg.mProcessed ){ripplePlan.add(pkg);results.planTime += pkg.mPkg.mBuildTime;pkg.mProcessed = true;// How do we handle duplicates ?ArrayList<PlannedPackage> usedBy = usedByPackages(pkg, results.planCollection);toBuild.addAll(usedBy);}}}}else{// Escrow// Add all (not excluded) packages directly to the ripplePlanfor (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); ){Package p = it.next();if (!p.mIsProcessed) {ripplePlan.add(new PlannedPackage(p));}}}//// Now we have a collection of packages to build (and only the packages to build)// Need to determine the order in which it can be done and assign build levels// Basically:// Scan the collection looking for any package whose dependencies do not exist in the collection// or they have been allocated a build level.//// Need to do this in waves so that we can correctly determine the build level.////// Need two metrics out of this process// buildGroup - all packages in a given group depend entirely on packages in lower numbers groups// buildLevel - This is a function of the number of parallel builds that can be performed// and//for (Iterator<PlannedPackage> it = ripplePlan.iterator(); it.hasNext(); ){PlannedPackage p = it.next();p.mProcessed = true;p.mBuildLevel = 0;}int buildLevel = 0;boolean more = true;do {ArrayList<PlannedPackage> thisLevel = new ArrayList<PlannedPackage>();for (Iterator<PlannedPackage> it = ripplePlan.iterator(); it.hasNext(); ){PlannedPackage p = it.next();if ( p.mProcessed ){boolean found = false;for (Iterator<PkgDependency> it2 = p.mPkg.mDependencyCollection.iterator(); !found && it2.hasNext(); ){PkgDependency depEntry = it2.next();for (Iterator<PlannedPackage> it3 = ripplePlan.iterator(); !found && it3.hasNext(); ){PlannedPackage pkg = it3.next();if (pkg.mPkg.mAlias.compareTo( depEntry.alias ) == 0){found = pkg.mProcessed ;break;}}}if (!found){// None of this packages dependencies can be found in the collection of packages to build// Thus we can build it - at the current levelp.mBuildLevel = buildLevel;thisLevel.add(p);}}}// Mark all the packages at this level as have been processed// Cannot do this while we are determining the packages in the levelfor (Iterator<PlannedPackage> it = thisLevel.iterator(); it.hasNext(); ){PlannedPackage p = it.next();p.mProcessed = false;}//// If any packages needed to be build at this level, then there might be a higher level// If no packages needed to be build at this level, then there is nothing to do.more = !thisLevel.isEmpty();buildLevel++;} while (more);// Sort the packages by buildOrder and buildTimephase.setPhase("Sort Plan");Collections.sort(ripplePlan, PlannedPackage.BuildOrderComparitor);// Optionally - Display the calculated plan// It will fill the log, but ...if (mLogger.isInfoEnabled() ){phase.setPhase("Display Build Order");mLogger.info("Plan Build {} Time: {}", name, results.planTime);for (Iterator<PlannedPackage> it = ripplePlan.iterator(); it.hasNext(); ){PlannedPackage p = it.next();mLogger.info("Plan: {} {} t:{}", p.mBuildLevel, p.mPkg.mAlias, p.mPkg.mBuildTime);}}results.planCollection = ripplePlan;phase.setPhase("EndPlanCollection");return results;}/** Generate a plan based on a modified set of packages** @param name - Name of the plan* @param basicPlan - basicPlan* @param mode - True: Include natural ripples and WIPS/RIPPLES that would have been done* as a part of the basic plan* @return*/private PlanResults postRipplePlan(String name, PlanResults basicPlan, boolean mode) {PackageCollection fullPlanCollection = new PackageCollection(mPackageCollection);ArrayList<Package> buildCandidates = new ArrayList<Package>();buildCandidates.addAll(mPackageCollectionWip.mCollection);buildCandidates.addAll(mPackageCollectionRipple.mCollection);if ( !buildCandidates.isEmpty() ){for (Iterator<Package> it = buildCandidates.iterator(); it.hasNext(); ){Package p = it.next();if (p.mBuildFile >= 0){boolean include = true;if (!mode){// Exclude packages that would have been processed in the basicPlanfor (Iterator<PlannedPackage> it1 = basicPlan.planCollection.iterator(); it1.hasNext(); ){PlannedPackage pkg = it1.next();if (pkg.mPkg == p){mLogger.warn("Test Plan without {}", p);include = false;break;}}}if( include ){mLogger.warn("Test Plan with {}", p);fullPlanCollection.upsert(p);Package.resetCircularDependency (fullPlanCollection);if ( p.hasCircularDependency( fullPlanCollection ) ){mLogger.info("planRelease circular dependency detected {}", p);// Force this package to be marked as having a circular dependency - even if its been excludedp.mBuildFile = 0;// Exclude the package// max 50 charsrippleBuildExclude(p, p.mId, "Package has circular dependency", -6);}}}}}return planCollection(name, fullPlanCollection, mode);}/*** Calculate a collection of packages that actively use the named package* A consumer package does NOT use the named package,* If the package is marked as advisoryRipple* If the consumer is an SDK or is Pegged* If the consumer cannot be built** Used when planning which packages need to be built** @param pkg - Package to process* @param planCollection - collection of packages to scan** @return A collection of packages that actively 'use' the specified package*/private ArrayList<PlannedPackage> usedByPackages(PlannedPackage pkg, ArrayList<PlannedPackage> planCollection) {ArrayList<PlannedPackage> usedBy = new ArrayList<PlannedPackage>();// If 'this' package is marked as advisory, then don't ripple its consumersif ( !pkg.mPkg.mAdvisoryRipple ){for (Iterator<PlannedPackage> it = planCollection.iterator(); it.hasNext(); ){PlannedPackage p = it.next();// If the (potential) consumer is an SDK or is Pegged, then we don't ripple its consumerif (!(p.mPkg.mIsSdk || p.mPkg.mIsPegged)){// Is this package 'actively used' in the current buildif (p.mPkg.mBuildFile >= 0){for (Iterator<PkgDependency> it2 = p.mPkg.mDependencyCollection.iterator(); it2.hasNext(); ){PkgDependency depEntry = it2.next();if ( pkg.mPkg.mAlias.compareTo( depEntry.alias ) == 0 ) {// Have found a consumer of 'pkg'usedBy.add(p);break;}}}}}}return usedBy;}/*** Calculate a collection of packages that use the named package** Used when determining which packages to exclude from a build** @param pkg - Package to process* @param packageCollection - collection of packages to scan** @return A collection of packages that actively 'use' the specified package*/private ArrayList<Package> usedByAnyPackages(Package pkg, PackageCollection packageCollection) {ArrayList<Package> usedBy = new ArrayList<Package>();for (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); ){Package p = it.next();for (Iterator<PkgDependency> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); ){PkgDependency depEntry = it2.next();if ( pkg.mAlias.compareTo( depEntry.alias ) == 0 ) {usedBy.add(p);break;}}}return usedBy;}/*** Locate Daemon Instructions ( TEST and RIPPLE ) Requests that cannot be satisfied - will not be built due to* errors in dependent packages. Report the error to the user and remove the request** These need o be rejected now as the the Master logic will plan a build a build if there is a daemon instruction* present. If an instruction cannot be processed and is not removed, then the daemon will continuously 'plan'*/private void reportUnsatisfiedDaemonInstructions() throws SQLException, Exception {PackageCollection toProcess = new PackageCollection();toProcess.addAll(mPackageCollectionTest);toProcess.addAll(mPackageCollectionRipple);for (Iterator<Package> it = toProcess.iterator(); it.hasNext(); ){Package p = it.next();//// If the package is in the 'plan, then its not excluded//boolean isPlanned = false;//TODO Should mBuildOrder be aPackageCollectionfor (Iterator<PlannedPackage> it1 = mBuildOrder.listIterator(); it1.hasNext(); ){PlannedPackage pkg = it1.next();if (pkg.mPkg == p){isPlanned = true;break;}}if (! isPlanned){// If this package has been excluded, then we can't build it//boolean excluded = false;for (ListIterator<BuildExclusion> it1 = mBuildExclusionCollection.listIterator(); it1.hasNext(); ){BuildExclusion be = it1.next();if (be.compare(p.mId)){excluded = true;if (p.mBuildFile >= 0)p.mBuildFile = -5;break;}}if (excluded){String reason;switch (p.mBuildFile){case -1: reason = "Not reproducible"; break;case -2: reason = "Not reproducible on configured build platforms"; break;case -3: reason = "Marked as 'Do not ripple'"; break;case -4: reason = "Dependent on a package not in the release"; break;case -5: reason = "Indirectly dependent on a package not reproducible in the release"; break;case -6: reason = "Has a circular dependency"; break;case -7: reason = "Pegged or SDK package not in dpkg_archive"; break;case -8: reason = "Is a Pegged or SDK package"; break;case -9: reason = "Rejected Daemon Instruction"; break;case -10: reason = "Unbuildable package not in dpkg_archive"; break;case -11: reason = "Marked as 'RippleStop'"; break;case -12: reason = "Cannot generate next version number"; break;default: reason = "Unknown reason. Code:" + p.mBuildFile; break;}mLogger.warn("planRelease Test Build of an unbuildable of package deleted: {}", p);int daemonInstruction = p.mTestBuildInstruction > 0 ? p.mTestBuildInstruction : p.mForcedRippleInstruction;if ( daemonInstruction <= 0){mLogger.warn("Daemon Instruction number not found for {}", p);}mReleaseManager.markDaemonInstCompleted( daemonInstruction );emailRejectedDaemonInstruction(reason,p);}}}}/** Determine if a given PVID is a member of the current release.* Used to determine if a package dependency is out of date** @param dpvId* @return true - specified pvid is a full member of the Release*/private boolean isInRelease(Integer dpvId) {boolean inRelease = ( mPackageCollection.contains(dpvId) != null);return inRelease;}/** Reports what change in build exceptions happens as part of planRelease** There are three types of exceptions* PlanError - These may be removed, if the error was not seen in the last plan* BuildErrors - These we persist* IndirectErrors - Packages that depend on a Plan or Build errors*/public void reportChange() throws SQLException, Exception{int counter = 0;for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); ){BuildExclusion buildExclusion = it.next();// Skip 'Processed' entries// These will be the result of a PlanError that we have seen again//if ( buildExclusion.isProcessed() ) {continue;}// BuildErrors - Persist ( do nothing) These are handled elsewhere// PackageErrors - Add new (ie: was not imported)// IndirectErrors - Add/Remove as detected//if (buildExclusion.isABuildError()) {continue;} else if (buildExclusion.isAPackageError()) {if (buildExclusion.isImported()) {continue;}}if (buildExclusion.isImported()) {// Remove from the exclusion listmLogger.warn("reportChange remove unused exclusion: {}", buildExclusion );buildExclusion.includeToBuild(mReleaseManager, mBaseline);} else {// Exclude and notifymLogger.warn("reportChange add new exclusion: {}", buildExclusion );buildExclusion.excludeFromBuild(mReleaseManager, mBaseline);buildExclusion.email(this, mPackageCollection);counter++;}}mLogger.warn("reportChange exclusion count: {}", counter);}/**reports the build plan*/public void reportPlan() throws SQLException, Exception{mReleaseManager.reportPlan(mRtagId, mBuildOrder);}/**Returns the first build file from the collection* The build file will be flagged as empty if none exists*/public BuildFile getFirstBuildFileContent(){mLogger.debug("getFirstBuildFileContent");mBuildIndex = -1;return getNextBuildFileContent();}/**Returns next build file from the collection* The build file will be flagged as empty if none exists*/public BuildFile getNextBuildFileContent(){mLogger.debug("getNextBuildFileContent");BuildFile retVal = null;try{mBuildIndex++;retVal = mBuildCollection.get( mBuildIndex );}catch( IndexOutOfBoundsException e ){// Ignore exception. retVal is still null.}if (retVal == null){retVal = new BuildFile();}mLogger.debug("getNextBuildFileContent returned {}", retVal.state.name() );return retVal;}/**collects meta data associated with the baseline* this is sufficient to send an indefinite pause email notification** Escrow: Used once to collect information about the SBOM and associated Release* mBaseline is an SBOMID* Daemon: Used each build cycle to refresh the information* mBaseline is an RTAGID*/public void collectMetaData() throws SQLException, Exception{Phase phase = new Phase("cmd");mLogger.debug("collectMetaData mDaemon {}", mDaemon);try{phase.setPhase("connect");mReleaseManager.connect();if (! mDaemon){mSbomId = mBaseline;phase.setPhase("queryRtagIdForSbom");mRtagId = mReleaseManager.queryRtagIdForSbom(mBaseline);if (mRtagId == 0){mLogger.error("SBOM does not have a matching Release. Cannot be used as a base for an Escrow");throw new Exception("rtagIdForSbom show stopper. SBOM does not have an associated Release");}}phase.setPhase("queryReleaseConfig");mReleaseManager.queryReleaseConfig(mRtagId);if (mDaemon){phase.setPhase("queryMailServer");setMailServer(mReleaseManager.queryMailServer());phase.setPhase("queryMailSender");setMailSender(mReleaseManager.queryMailSender());phase.setPhase("queryGlobalAddresses");setMailGlobalTarget(mReleaseManager.queryGlobalAddresses());phase.setPhase("queryProjectEmail");mMailGlobalCollection = mReleaseManager.queryProjectEmail(mBaseline);phase.setPhase("mMailGlobalTarget");mMailGlobalCollection.add(0,getMailGlobalTarget());phase.setPhase("mPlanControl");mReleaseManager.queryPlanControl(mBaseline, mPlanControl);}phase.setPhase("queryBaselineName");mBaselineName = mReleaseManager.queryBaselineName(mBaseline);phase.setPhase("Done");}finally{// this block is executed regardless of what happens in the try block// even if an exception is thrown// ensure disconnectmReleaseManager.disconnect();}}/*** Sets the mBuildFile to the specified value for the package* Does not handle dependent packages - this will be done later** @param p The package being excluded* @param rootPvId The PVID of the package that is causing the exclusion. Null or -ve values are special* This package is the root cause, -2: Excluded by Ripple Stop* @param rootCause Text message. Max 50 characters imposed by RM database* @param reason New value for mBuildFile*/private void rippleBuildExclude(Package p, int rootPvId, String rootCause, int reason ){mLogger.debug("rippleBuildExclude");if ( p.mBuildFile >= 0 ){p.mBuildFile = reason;mLogger.info("rippleBuildExclude set mBuildFile to {} for package {}", reason, p.mAlias );// Scan the complete collection looking for a matching item// If found then assume that this error is a PlanError that is still present// Mark it as Processed to indicate that its still present// If found, process it, else add it (unprocessed)boolean found = false;for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); ){BuildExclusion buildExclusion = it.next();if ( buildExclusion.compare(p.mId, rootPvId, rootCause)){buildExclusion.setProcessed();found = true;break;}}if (!found){// Entry not found in the mBuildExclusionCollection. Its a new error//// Mark all occurrences for this package as processed// These will be superseded by a new build exclusion entryfor (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); ){BuildExclusion buildExclusion = it.next();if ( buildExclusion.compare(p.mId)){buildExclusion.setProcessed();}}// Add the new build exclusion to a listBuildExclusion buildExclusion = new BuildExclusion(p.mId, rootPvId, rootCause, p.mTestBuildInstruction);mBuildExclusionCollection.add(buildExclusion);}}mLogger.info("rippleBuildExclude set {} {}", p.mAlias, p.mBuildFile);}/**Simple XML string escaping** @param xml - String to escape* @return - A copy of the string with XML-unfriendly characters escaped*/public static String escapeXml( String xml ){xml = xml.replaceAll("&", "&");xml = xml.replaceAll("<", "<");xml = xml.replaceAll(">", ">");xml = xml.replaceAll("\"",""");xml = xml.replaceAll("'", "'");xml = xml.replaceAll("\\$", "\\$\\$");return xml;}/** Quote a string or a string pair* If two strings are provided, then they will be joined with a comma.** @param text - First string to quote* @param text2 - Optional, second string* @return A string of the form 'text','text2'*/public static String quoteString(String text, String text2){String result;result = "\'" + text + "\'";if (text2 != null ){result += ",\'" + text2 + "\'";}return result;}/** Generate build file information**/private void generateBuildFiles(){// persist the build filesboolean allProcessed = false;int buildFile = 1;StringBuilder rawData = new StringBuilder();StringBuilder setUp = new StringBuilder();mLogger.debug("generateBuildFiles");if ( mDaemon ){// all interesting packages in daemon mode match the following filterbuildFile = 3;}//-----------------------------------------------------------------------// Generate the build filedo{BuildFile buildEntry = new BuildFile();buildEntry.state = BuildFileState.Dummy;XmlBuilder xml = generateBuildFileHeader();// Generate properties for each package in this build level or lower build levels// The properties link the packageAlias to the PackageName and PackageVersion//// [DEVI 54816] In escrow mode all unreproducible packages are includedfor (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); ){Package p = it.next();if ( ( ( p.mBuildFile > 0 ) && ( p.mBuildFile <= buildFile ) ) || ( !mDaemon && p.mBuildFile == -2 ) ){xml.addProperty(p.mAlias, p.mName + " " + p.mVersion + p.mExtension);}}// UTF Support// Insert additional info into the build file to provide extra checking//if ( ! mReleaseManager.mUseDatabase ){// UTF Support// Insert per-package planning information//xml.addComment("mPackageCollection");for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); ){Package p = it.next();generatePackageInfo(xml, p);}xml.addComment("mPackageCollectionWip");for (Iterator<Package> it = mPackageCollectionWip.iterator(); it.hasNext(); ){Package p = it.next();if (p.mIsNotReleased )generatePackageInfo(xml, p);}xml.addComment("mPackageCollectionTest");for (Iterator<Package> it = mPackageCollectionTest.iterator(); it.hasNext(); ){Package p = it.next();if (p.mIsNotReleased )generatePackageInfo(xml, p);}xml.addComment("mPackageCollectionTestRipple");for (Iterator<Package> it = mPackageCollectionRipple.iterator(); it.hasNext(); ){Package p = it.next();if (p.mIsNotReleased )generatePackageInfo(xml, p);}// UTF Support// Insert build exclusion informationxml.addComment("mBuildExclusionCollection");for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); ){BuildExclusion buildExclusion = it.next();xml.addComment(buildExclusion.toString());}}// UTF Support (Also while trialing the changes)// Insert build Planif (mDaemon){xml.addComment("mBuildOrder");for (Iterator<PlannedPackage> it = mBuildOrder.iterator(); it.hasNext(); ){PlannedPackage p = it.next();String comment ="pvid="+ p.mPkg.mId +" order=" + p.mBuildLevel +" time=" + p.mPkg.mBuildTime +" name=\"" + p.mPkg.mAlias + "\"";xml.addComment(comment);}}// Generate Taskdef informationgenerateTaskdef(xml);//// Insert known Machine Information// Escrow usage:// Map machType to machClass// Also serves as a snapshot of the required build configuration// ie: machine types and buildfilters//if (!mDaemon){generateMachineInfo(xml, null);}//// Generate target rules for each package to be built within the current build file//boolean daemonHasTarget = false;for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile > 0 && p.mBuildFile <= buildFile ){generateTarget(xml, buildEntry, p, buildFile);if ( p.mBuildFile == 1 ){daemonHasTarget = true;// Retain information about the target packagebuildEntry.mPkgId = p.mPid;buildEntry.mPvId = p.mId;}}// Generate escrow extraction commands// Only done on the first pass though the packages//if ( !mDaemon && buildFile == 1 ){setUp.append("jats jats_vcsrelease -extractfiles"+ " \"-label=" + p.mVcsTag + "\""+ " \"-view=" + p.mAlias + "\""+ " -root=. -noprefix"+ mlf );}// Generate escrow raw CSV data// Note: I don't think this data is used at allif ( !mDaemon && ( p.mBuildFile == buildFile)){StringAppender machines = new StringAppender(",");for (Iterator<BuildStandard> it1 = p.mBuildStandardCollection.iterator(); it1.hasNext();){BuildStandard bs = it1.next();machines.append(bs.mMachClass);}rawData.append(p.mAlias + "," +buildFile + "," +machines +mlf);}}if ( mDaemon && !daemonHasTarget ){// must have AbtTestPath, AbtSetUp, AbtTearDown, and AbtPublish targetsXmlBuilder target = xml.addNewElement("target");target.addAttribute("name", "AbtTestPath");target = xml.addNewElement("target");target.addAttribute("name", "AbtSetUp");target = xml.addNewElement("target");target.addAttribute("name", "AbtTearDown");target = xml.addNewElement("target");target.addAttribute("name", "AbtPublish");}generateDefaultTarget( xml, buildFile);// Convert the Xml structure into text and save it in the build file// Add this build file to the mBuildCollectionbuildEntry.content = mXmlHeader + xml.toString();mBuildCollection.add(buildEntry);// are more build files requiredallProcessed = true;if (!mDaemon){// this is escrow mode centricfor (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile > buildFile ){// more build files are requiredallProcessed = false;mLogger.info("planRelease reiterating package has no build requirement {} {} {}",p.mName, p.mBuildFile, buildFile);break;}}buildFile++;}} while( !allProcessed );// Save additional escrow dataif ( !mDaemon ){mEscrowSetup = setUp.toString();mEscrowRawData = rawData.toString();}}/**returns a build file header for the mBaseline*/private XmlBuilder generateBuildFileHeader(){mLogger.debug("generateBuildFileHeader");XmlBuilder element = new XmlBuilder("project");element.addAttribute("name", "mass");element.addAttribute("default", "full");element.addAttribute("basedir", ".");if ( mDaemon ){element.addProperty("abt_mail_server", getMailServer());element.addProperty("abt_mail_sender", getMailSender());element.addProperty("abt_rtag_id", mBaseline);element.addProperty("abt_daemon", mReleaseManager.currentTimeMillis());element.makePropertyTag("abt_packagetarball", true);element.makePropertyTag("abt_usetestarchive", !ReleaseManager.getUseMutex());for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile == 1 ){element.addProperty("abt_package_name", p.mName);element.addProperty("abt_package_version", p.mVersion + p.mExtension);element.addProperty("abt_package_extension", p.mExtension);element.addProperty("abt_package_location", getBuildLocation(p));element.addProperty("abt_package_ownerlist", p.emailInfoNonAntTask(this));element.addProperty("abt_package_build_info", buildInfoText(p));// depends in the form 'cs','25.1.0000.cr';'Dinkumware_STL','1.0.0.cots'StringAppender depends = new StringAppender(";");for (Iterator<PkgDependency> it3 = p.mDependencyCollection.iterator(); it3.hasNext(); ){PkgDependency depEntry = it3.next();depends.append( quoteString(depEntry.pkg.mName, depEntry.pkg.mVersion + depEntry.pkg.mExtension) );}element.addProperty("abt_package_depends", depends.toString());element.addProperty("abt_is_ripple", p.mDirectlyPlanned ? "0" : "1");element.addProperty("abt_build_reason", p.mBuildReason.toString());element.addProperty("abt_package_version_id", p.mId);element.addProperty("abt_does_not_require_source_control_interaction", ! p.mRequiresSourceControlInteraction ? "true" : "false" );element.addProperty("abt_test_build_instruction", p.mTestBuildInstruction);element.addProperty("abt_vcs_tag", p.mVcsTag);}}}else{// Escrow Modeelement.addProperty("abt_rtag_id", mRtagId);element.addProperty("abt_sbom_id", mSbomId);}element.addProperty("abt_release", escapeXml(mBaselineName));element.addProperty("abt_buildtool_version", mReleaseManager.getMajorVersionNumber() );element.addNewElement("condition").addAttribute("property", "abt_family").addAttribute("value", "windows").addNewElement("os").addAttribute("family", "windows");element.addProperty("abt_family", "unix");return element;}/** Add task def XML items taskdef for the abt ant task* @param xml - xml item to extend*/private void generateTaskdef(XmlBuilder xml){mLogger.debug("generateTaskdef");xml.addNewElement("taskdef").addAttribute("name", "abt").addAttribute("classname", "com.erggroup.buildtool.abt.ABT");xml.addNewElement("taskdef").addAttribute("name", "abtdata").addAttribute("classname", "com.erggroup.buildtool.abt.ABTData");}/** returns the command abtdata items* <br>Common machine information* <br>Common email address*** @param xml - Xml element to extend* @param p - Package (May be null)*/private void generateMachineInfo(XmlBuilder xml, Package p){XmlBuilder element = xml.addNewElement("abtdata");element.addAttribute("id", "global-abt-data");//// Iterate over all the machines and create a nice entry//for (Iterator<ReleaseConfig> it = mReleaseManager.mReleaseConfigCollection.mReleaseConfig.iterator(); it.hasNext(); ){ReleaseConfig rc = it.next();element.addElement(rc.getMachineEntry());}//// Now the email information//if ( p != null){p.emailInfo(this, element);}}/** Generate package information* Only when running unit tests** @param xml - An xml element to append data to* @param p - Package to process*/private void generatePackageInfo (XmlBuilder xml, Package p){StringBuilder comment = new StringBuilder();StringBuilder deps = new StringBuilder();String joiner = "";for (Iterator<PkgDependency> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); ){PkgDependency depEntry = it2.next();deps.append(joiner).append(depEntry.alias);joiner = ",";}comment.append("pvid=").append(p.mId);comment.append(" name=").append('"').append(p.mAlias).append('"');comment.append(" reason=").append(p.mNoBuildReason);comment.append(" buildFile=").append(p.mBuildFile);comment.append(" directlyPlanned=").append(p.mDirectlyPlanned);comment.append(" indirectlyPlanned=").append(p.mIndirectlyPlanned);comment.append(" depends=[").append(deps).append("]");xml.addComment(comment.toString());}/**returns an ant target for the passed Package* in daemon mode:* packages are categorized with one of three mBuildFile values:* 1 the package to be built by this buildfile* 2 the packages with a future build requirement* 3 the packages with no build requirement* the returned target depends on this categorization and will have* 1 full abt info* 2 full dependency info to determine future build ordering but no abt info (will not build this package)* 3 only a name attribute (will not build this package)* in escrow mode:* if the passed Package's mBuildFile is different (less than) the passed build file,* the returned target have only a name attribute (will not build this package)** @param xml - Xml element to extend* @param buildEntry - Record build type (dummy/generic/not generic)* @param p - Package to process* @param buildFile - buildfile level being processed*/private void generateTarget(XmlBuilder xml, BuildFile buildEntry, Package p, int buildFile){mLogger.debug("generateTarget");//---------------------------------------------------------------------// Generate the AbtData - common machine and email information// Only used by the daemon builds//if ( mDaemon && p.mBuildFile == 1 ){generateMachineInfo(xml, p );}//-------------------------------------------------------------------------// Generate the <target name=... /> construct// There are two types// 1) Simple dummy place holder. Just has the PackageName.PackageExt// 2) Full target. Has all information to build the package including// AbtSetUp, AbtTearDown and AbtPublish targets//if ( !mDaemon && ( p.mBuildFile < buildFile ) ){XmlBuilder target = xml.addNewElement("target");target.addAttribute("name", p.mAlias);}else{if (!mDaemon){// Escrow Only:// Generate the 'wrapper' target// This is used to ensure that the required dependencies have been built - I think//StringAppender dependList = new StringAppender(",");if ( !p.mDependencyCollection.isEmpty() ){for (Iterator<PkgDependency> it = p.mDependencyCollection.iterator(); it.hasNext(); ){PkgDependency depEntry = it.next();if ( !mDaemon && depEntry.pkg.mBuildFile == -2 ){// ignore targets which build in foreign environments in escrow modecontinue;}dependList.append(depEntry.pkg.mAlias);}}XmlBuilder target = xml.addNewElement("target").isExpanded();target.addAttribute("name", p.mAlias + ".wrap");if (dependList.length() > 0){target.addAttribute("depends", dependList.toString() );}if ( !mDaemon ){boolean hasDependenciesBuiltInThisIteration = false;if ( ( !p.mDependencyCollection.isEmpty()) ){for (Iterator<PkgDependency> it = p.mDependencyCollection.iterator(); it.hasNext(); ){PkgDependency depEntry = it.next();if ( depEntry.pkg.mBuildFile == buildFile ){hasDependenciesBuiltInThisIteration = true;break;}}}if ( hasDependenciesBuiltInThisIteration ){XmlBuilder condition = target.addNewElement("condition");condition.addAttribute("property", p.mAlias + ".build");XmlBuilder and = condition.addNewElement("and");for (Iterator<PkgDependency> it = p.mDependencyCollection.iterator(); it.hasNext(); ){PkgDependency depEntry = it.next();if ( depEntry.pkg.mBuildFile == buildFile ){XmlBuilder or = and.addNewElement("or");XmlBuilder equal1 = or.addNewElement("equals");equal1.addAttribute("arg1", "${" + depEntry.pkg.mAlias + ".res}");equal1.addAttribute("arg2", "0");XmlBuilder equal2 = or.addNewElement("equals");equal2.addAttribute("arg1", "${" + depEntry.pkg.mAlias + ".res}");equal2.addAttribute("arg2", "257");}}}else{target.addProperty(p.mAlias + ".build", "");}}}//// Generate the 'body' of the target package// Escrow Mode: Always// Daemon Mode: Only for the one target we are building// Simplifies the XML// Reduces noise in the logs// Don't add target dependencies.// We are only building one target and the// dependency management has been done way before now.// All it does is makes the logs noisy.//if ( ( mDaemon && p.mBuildFile == 1 ) || !mDaemon ){XmlBuilder target = xml.addNewElement("target").isExpanded();target.addAttribute("name", p.mAlias);if ( !mDaemon ){target.addAttribute("depends", p.mAlias + ".wrap");target.addAttribute("if",p.mAlias + ".build");}if ( mDaemon && p.mBuildFile == 1 ){target.addProperty(p.mAlias + "pkg_id",p.mPid);target.addProperty(p.mAlias + "pv_id",p.mId);}target.addProperty(p.mAlias + "packagename",p.mName);target.addProperty(p.mAlias + "packageversion",p.mVersion);target.addProperty(p.mAlias + "packageextension",p.mExtension);target.addProperty(p.mAlias + "packagevcstag",p.mVcsTag);target.makePropertyTag(p.mAlias + "directchange", p.mDirectlyPlanned);target.makePropertyTag(p.mAlias + "doesnotrequiresourcecontrolinteraction", ! p.mRequiresSourceControlInteraction);buildEntry.state = BuildFile.BuildFileState.NonGeneric;if ( p.isGeneric() ){buildEntry.state = BuildFile.BuildFileState.Generic;target.makePropertyTag(p.mAlias + "generic", true);}target.addProperty(p.mAlias + "loc", getBuildLocation(p));target.makePropertyTag(p.mAlias + "unittests", p.mHasAutomatedUnitTests && mDaemon);// Add our own task and associated information//XmlBuilder abt = target.addNewElement("abt").isExpanded();for (Iterator<PkgDependency> it = p.mDependencyCollection.iterator(); it.hasNext(); ){PkgDependency depEntry = it.next();XmlBuilder depend = abt.addNewElement("depend");depend.addAttribute("package_alias", "${" + depEntry.pkg.mAlias + "}");}buildInfo(abt,p);if ( mDaemon && p.mBuildFile == 1 ){// AbtTestPathtarget = xml.addNewElement("target").isExpanded();target.addAttribute("name", "AbtTestPath");target.addProperty("AbtTestPathpackagevcstag", p.mVcsTag);target.addProperty("AbtTestPathpackagename", p.mName);abt = target.addNewElement("abt").isExpanded();buildInfo(abt, p);// AbtSetUptarget = xml.addNewElement("target").isExpanded();target.addAttribute("name", "AbtSetUp");target.addProperty("AbtSetUppackagevcstag", p.mVcsTag);target.addProperty("AbtSetUppackagename", p.mName);abt = target.addNewElement("abt").isExpanded();buildInfo(abt, p);// AbtBuildInfotarget = xml.addNewElement("target").isExpanded();target.addAttribute("name", "AbtBuildInfo");target.addProperty("AbtBuildInfo"+"packagevcstag", p.mVcsTag);target.addProperty("AbtBuildInfo"+"packagename", p.mName);target.addProperty("AbtBuildInfo"+"packageversion", p.mVersion);target.addProperty("AbtBuildInfo"+"packageextension", p.mExtension);target.makePropertyTag("AbtBuildInfo"+"generic", p.isGeneric());target.addProperty("AbtBuildInfo"+"loc", getBuildLocation(p));abt = target.addNewElement("abt").isExpanded();buildInfo(abt, p);// AbtTearDowntarget = xml.addNewElement("target").isExpanded();target.addAttribute("name", "AbtTearDown");target.addProperty("AbtTearDownpackagevcstag", p.mVcsTag);target.addProperty("AbtTearDownpackagename", p.mName);target.addProperty("AbtTearDownpackageversion", p.mVersion);target.addProperty("AbtTearDownpackageextension", p.mExtension);target.makePropertyTag(p.mAlias + "generic", p.isGeneric());abt = target.addNewElement("abt").isExpanded();buildInfo(abt, p);// AbtPublishtarget = xml.addNewElement("target").isExpanded();target.addAttribute("name", "AbtPublish");target.addProperty("AbtPublishpackagevcstag", p.mVcsTag);target.addProperty("AbtPublishpackagename", p.mName);target.addProperty("AbtPublishpackageversion", p.mVersion);target.addProperty("AbtPublishpackageextension", p.mExtension);target.makePropertyTag("AbtPublishdirectchange", p.mDirectlyPlanned);target.makePropertyTag("AbtPublishdoesnotrequiresourcecontrolinteraction", ! p.mRequiresSourceControlInteraction);target.makePropertyTag("AbtPublishgeneric", p.isGeneric());target.addProperty("AbtPublishloc", getBuildLocation(p));abt = target.addNewElement("abt").isExpanded();buildInfo(abt, p);}}}}/** Extends the xml object. Adds ant default target for the current build iteration* @param xml - The XmlBuilder Object to extend* @param buildFile - The current build file level. This differs for Daemon and Escrow mode. In Daemon mode it will not be a '1'*/private void generateDefaultTarget(XmlBuilder xml, int buildFile){mLogger.debug("generateDefaultTarget");XmlBuilder target = xml.addNewElement("target").isExpanded();target.addAttribute("name", "fullstart");if (buildFile == 1){antEcho(target, "${line.separator}" + mAnyBuildPlatforms + "${line.separator}${line.separator}");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile == -1 ){antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");}}antEcho(target, "${line.separator}" + mAssocBuildPlatforms + "${line.separator}${line.separator}");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile == -2 ){antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");}}antEcho(target, "${line.separator}" + mNotInBaseline + "${line.separator}${line.separator}");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile == -4 ){antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");}}antEcho(target, "${line.separator}" + mDependent + "${line.separator}${line.separator}");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( p.mBuildFile == -5 ){antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");}}}if ( !mDaemon ){antEcho(target, "${line.separator}Build Started:${line.separator}${line.separator}");}//// Create a comma separated list of all the required targets// Escrow : All packages// Daemon : Just the package being built//StringAppender dependList = new StringAppender(",");dependList.append("fullstart");for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package p = it.next();if ( ( p.mBuildFile > 0 ) && ( p.mBuildFile <= buildFile ) ){if ((mDaemon && p.mBuildFile == 1) || !mDaemon){dependList.append(p.mAlias);}}}target = xml.addNewElement("target").isExpanded();target.addAttribute("name", "full");target.addAttribute("depends", dependList.toString());if ( !mDaemon ){antEcho(target, "${line.separator}Build Finished${line.separator}");}}/** Internal helper function to create an ant 'echo statement* Many of the parameters are fixed*/private void antEcho( XmlBuilder xml, String message){XmlBuilder msg = xml.addNewElement("echo");msg.addAttribute("message", message);msg.addAttribute("file", "publish.log");msg.addAttribute("append", "true");}/**sets the mIndirectlyPlanned true for the package and all dependent packages*/private void rippleIndirectlyPlanned(Package p){mLogger.debug("rippleIndirectlyPlanned");if ( !p.mIndirectlyPlanned && p.mBuildFile == 0 ){p.mIndirectlyPlanned = true;for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); ){Package pkg = it.next();if ( pkg != p ){for (Iterator<PkgDependency> it2 = pkg.mDependencyCollection.iterator(); it2.hasNext(); ){PkgDependency depEntry = it2.next();if ( depEntry.pkg == p ){rippleIndirectlyPlanned( pkg );break;}}}}}mLogger.info("rippleIndirectlyPlanned set {} {}", p.mName, p.mIndirectlyPlanned);}/**accessor method*/public String getEscrowSetUp(){mLogger.debug("getEscrowSetUp");String retVal = mEscrowSetup;mLogger.debug("getEscrowSetUp returned {}", retVal);return retVal;}/**accessor method*/public String getRawData(){mLogger.debug("getRawData");String retVal = mEscrowRawData;mLogger.debug("getRawData returned {}", retVal);return retVal;}/**Get the build loc (location)* This is package specific and will depend on the build mode (Escrow/Daemon)** @param p - Package being built* @return A string that describes the build location*/private String getBuildLocation(Package p){mLogger.debug("locationProperty");String location = "";if (mDaemon){// Daemon: Start in root of view/workspacelocation += mBaseline;}else{// Escrow: mAlias used with jats -extractfiles -viewlocation += p.mAlias;}//// Always use '/' as a path separator - even if user has specified '\'// Ant can handle it.//location = location.replace('\\', '/');return location;}/**Adds package build into as XML elements* @param xml - Xml element to extend* @param p - Package to process*/private void buildInfo(XmlBuilder xml, Package p){mLogger.debug("buildInfo");//// Create the xml build information// <platform gbe_machtype="linux_i386" type="jats" arg="all"/>//for (Iterator<BuildStandard> it = p.mBuildStandardCollection.iterator(); it.hasNext();){BuildStandard bs = it.next();bs.getBuildStandardXml(xml);}}/**returns the buildInfo as a single line of text* Used for reporting purposes only* @param p - Package to process**/public String buildInfoText(Package p){mLogger.debug("buildInfoText");StringAppender result = new StringAppender (";");//// Create platform:standards//for (Iterator<BuildStandard> it = p.mBuildStandardCollection.iterator(); it.hasNext(); ){BuildStandard bs = it.next();if ( bs.isActive() ){String info = bs.getBuildStandardText();result.append(info);}}mLogger.debug("buildInfoText returned {}", result);return result.toString();}/**prints to standard out in escrow mode only* <br>Prints a title and information. The title is only printed once.** @param header - The message title to display, if printMessage is true* @param text - A package name. Really just the 2nd line of the message* @param printHeader - Controls the printing of the message argument*/private void standardOut(final String header, final String text, boolean printHeader){mLogger.debug("standardOut");if (!mDaemon){if ( printHeader ){System.out.println(header);}System.out.println(text);}}/*** Email users about a rejected daemon instruction* @param reason - Reason for the rejection* @param pkg - Package to be affected by the instruction**/public void emailRejectedDaemonInstruction(String reason, Package p){mLogger.debug("emailRejectedDaemonInstruction");// Email SubjectString subject = "BUILD FAILURE of Daemon Instruction on package " + p.mAlias;// Email BodyString mailBody = "The build system reject the the Daemon Instruction";mailBody += "<p>Release: " + mBaselineName+ "<br>Package: " + p.mAlias+ "<br>Cause : " + reason+ "<br>Rm Ref: " + CreateUrls.generateRmUrl(getRtagId(), p.mId);mailBody += "<p><hr>";String target = p.emailInfoNonAntTask(this);mLogger.info("emailRejectedDaemonInstruction Server: {}", getMailServer());mLogger.info("emailRejectedDaemonInstruction Sender: {}", getMailSender());mLogger.warn("emailRejectedDaemonInstruction Target: {}", target);try{//Smtpsend.send(getMailServer(), // mailServergetMailSender(), // sourcetarget, // targetgetMailSender(), // ccnull, // bccsubject, // subjectmailBody, // bodynull // attachment);} catch (Exception e){mLogger.warn("Email Failure: emailRejectedDaemonInstruction:{}", e.getMessage());}}/** Send an email notifying users that a rippleStop has been triggered* The use will need to take some action* @param p - Package*/private void emailRippleStop(Package p) {mLogger.debug("emailRippleStop");// Email SubjectString subject = "BUILD FAILURE on package " + p.mAlias;// Failure ReasonString reason = "Ripple Required. Waiting for user action";// Email BodyString mailBody = "<p>Release: " + mBaselineName+ "<br>Package: " + p.mAlias+ "<br>Cause : " + reason+ "<br>Rm Ref: " + CreateUrls.generateRmUrl(getRtagId(), p.mId);mailBody += "<p><hr>";String target = p.emailInfoNonAntTask(this);mLogger.info("emailRippleStop Server: {}", getMailServer());mLogger.info("emailRippleStop Sender: {}", getMailSender());mLogger.warn("emailRippleStop Target: {}", target);try{//Smtpsend.send(getMailServer(), // mailServergetMailSender(), // sourcetarget, // targetgetMailSender(), // ccnull, // bccsubject, // subjectmailBody, // bodynull // attachment);} catch (Exception e){mLogger.warn("Email Failure: emailRippleStop:{}", e.getMessage());}}/*** @return the mMailServer*/public String getMailServer() {return mMailServer;}/*** @param mMailServer the mMailServer to set*/public void setMailServer(String mMailServer) {this.mMailServer = mMailServer;}/*** @return the mMailSender*/public String getMailSender() {return mMailSender;}/*** @param mMailSender the mMailSender to set*/public void setMailSender(String mMailSender) {this.mMailSender = mMailSender;}/*** @return the mMailGlobalTarget*/public String getMailGlobalTarget() {return mMailGlobalTarget;}/*** @param mMailGlobalTarget the mMailGlobalTarget to set*/public void setMailGlobalTarget(String mMailGlobalTarget) {this.mMailGlobalTarget = mMailGlobalTarget;}}