Subversion Repositories DevTools

Rev

Rev 7077 | Rev 7079 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6914 dpurdie 1
package com.erggroup.buildtool.ripple;
2
 
3
import java.sql.SQLException;
4
import java.util.ArrayList;
5
import java.util.Collections;
6
import java.util.Iterator;
7
import java.util.List;
8
import java.util.ListIterator;
9
 
7033 dpurdie 10
import org.slf4j.Logger;
11
import org.slf4j.LoggerFactory;
6914 dpurdie 12
 
13
import com.erggroup.buildtool.ripple.BuildFile.BuildFileState;
14
import com.erggroup.buildtool.ripple.ReleaseManager.BuildReason;
15
import com.erggroup.buildtool.smtp.CreateUrls;
16
import com.erggroup.buildtool.smtp.Smtpsend;
17
import com.erggroup.buildtool.utilities.StringAppender;
18
import com.erggroup.buildtool.utilities.XmlBuilder;
19
 
20
/**Plans release impact by generating a set of Strings containing build file content.
21
 */
22
public class RippleEngine
23
{
24
 
25
    /**configured mail server
26
     * @attribute
27
     */
28
    private String mMailServer = "";
29
 
30
    /**configured mail sender user
31
     * @attribute
32
     */
33
    private String mMailSender = "";
34
 
35
    /**configured global email target
36
     * @attribute
37
     */
38
    private String mMailGlobalTarget = "";
39
 
40
    /** Vector of email addresses for global and project wide use
41
     *  Most emails will be sent to this list of email recipients
42
     */
43
    public List<String> mMailGlobalCollection = new ArrayList<String>();
44
 
45
    /**name associated with the baseline
46
     * @attribute
47
     */
48
    public String mBaselineName = "";
49
 
50
    /**Collection of build exceptions associated with the baseline
51
     * Used to determine (and report) what change in build exceptions happens as part of planRelease
52
     * Daemon centric
53
     * @aggregation shared
54
     * @attribute
55
     */
56
    ArrayList<BuildExclusion> mBuildExclusionCollection = new ArrayList<BuildExclusion>();
57
 
58
    /**Logger
59
     * @attribute
60
     */
7033 dpurdie 61
    private static final Logger mLogger = LoggerFactory.getLogger(RippleEngine.class);
6914 dpurdie 62
 
63
    /** Escrow information - commands to set up escrow
64
     * @attribute
65
     */
66
    private String mEscrowSetup;
67
 
68
    /** Escrow information - Raw data (May not be used)
69
     * @attribute
70
     */
71
    private String mEscrowRawData;
72
 
7048 dpurdie 73
    /** Collections of packages
6914 dpurdie 74
     */
75
    private ArrayList<Package> mPackageCollection = new ArrayList<Package>();
7048 dpurdie 76
    private ArrayList<Package> mPackageCollectionWip = new ArrayList<Package>();
77
    private ArrayList<Package> mPackageCollectionTest = new ArrayList<Package>();
78
    private ArrayList<Package> mPackageCollectionRipple = new ArrayList<Package>();
79
    private ArrayList<Package> mPackageCollectionAll = new ArrayList<Package>();
80
 
6914 dpurdie 81
 
82
    /**index to current String item
83
     * @attribute
84
     */
85
    private int mBuildIndex;
86
 
87
    /**Database abstraction
88
     * @attribute
89
     */
90
    ReleaseManager mReleaseManager;
91
 
92
    /**Baseline identifier (rtag_id for a release manager baseline, bom_id for deployment manager baseline)
93
     * @attribute
94
     */
95
    private int mBaseline;
96
 
97
    /** Escrow Only: SBOM_ID
98
     */
99
    private int mSbomId;
100
 
101
    /** RTAG_ID
102
     *  Set from mBaseline
103
     */
104
    private int mRtagId;
105
 
106
    /**When true, mBuildCollection contains one item based on a release manager rtag_id and contains a daemon property
107
     * When false, mBuildCollection contains at least one item based on a deployment manager bom_id
108
     * Will be accessed by the Package class to calculate its mAlias
109
     * @attribute
110
     */
111
    public boolean mDaemon;
112
 
113
    /**collection of build file content in String form
114
     * @attribute
115
     */
116
    private ArrayList<BuildFile> mBuildCollection = new ArrayList<BuildFile>();
117
 
118
    /** List of packages that we plan to build
119
     * Used to provide feedback into RM
120
     * Only the first entry is about to be built as we re-plan every cycle
121
     */
122
    private ArrayList<Package> mBuildOrder = new ArrayList  <Package>();
123
    /**Warning message
124
     * @attribute
125
     */
126
    private static final String mAnyBuildPlatforms = "Warning. The following package versions are not reproducible on any build platform: ";
127
 
128
    /**Flag to control output to standard out
129
     * @attribute
130
     */
131
    private boolean mAnyBuildPlatformsFlag = true;
132
 
133
    /**Warning message
134
     * @attribute
135
     */
136
    private static final String mAssocBuildPlatforms = "Warning. The following package versions are not reproducible on the build platforms associated with this baseline: ";
137
 
138
    /**Flag to control output to standard out
139
     * @attribute
140
     */
141
    private boolean mAssocBuildPlatformsFlag = true;
142
 
143
    /**Warning message
144
     * @attribute
145
     */
146
    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: ";
147
 
148
    /**Flag to control output to standard out
149
     * @attribute
150
     */
151
    private boolean mNotInBaselineFlag = true;
152
 
153
    /**Warning message
154
     * @attribute
155
     */
156
    private static final String mDependent = "Warning. The following package versions are not reproducible as they are directly/indirectly dependent upon not reproducible package versions: ";
157
 
158
    /**Flag to control output to standard out
159
     * @attribute
160
     */
161
    private boolean mDependentFlag = true;
162
 
163
    /**Warning message
164
     * @attribute
165
     */
166
    private static final String mCircularDependency = "Warning. The following package versions are not reproducible as they have circular dependencies: ";
167
 
168
    /**Flag to control output to standard out
169
     * @attribute
170
     */
171
    private boolean mCircularDependencyFlag = true;
172
 
173
    /** String used to terminate lines
174
     * @attribute
175
     */
176
    private static final  String mlf = System.getProperty("line.separator");
177
 
178
    /** XML File Prefix
179
     */
180
    private static final String mXmlHeader = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>" + mlf;
181
 
182
    /**RippleEngine constructor
183
     * @param releaseManager  - Associated releaseManager instance
184
     * @param rtagId         - Release Identifier
185
     * @param isDaemon        - Mode of operation. False: Escrow, True: Daemon
186
     */
187
    public RippleEngine(ReleaseManager releaseManager, int rtagId, boolean isDaemon)
188
    {
7048 dpurdie 189
        mLogger.debug("RippleEngine rtag_id {} isDaemon {}", rtagId, isDaemon);
6914 dpurdie 190
        mReleaseManager = releaseManager;
191
        mBaseline = rtagId;
192
        mRtagId = rtagId;
193
        mDaemon = isDaemon;
194
        mReleaseManager.setDaemonMode(mDaemon);
195
    }
196
 
197
    /**
198
     * getRtagId
7048 dpurdie 199
     * @return The rtagId of the Release attached to this instance of the RippleEngine
6914 dpurdie 200
     */
201
    public int getRtagId()
202
    {
203
        return mRtagId;
204
    }
205
 
206
    /**Plan what is to be built
207
     * 	<br>Discards all build file content
208
     * 	<br>Generates new build file content
209
     * 
210
     * @param lastBuildActive 		- False. Daemon Mode. The last build was a dummy. 
211
     *                                This planning session may not result in a build
212
     *                              - True. Daemon Mode. The last build was not a dummy.
213
     *                                There is a very good chance that this planning session
214
     *                                will result in a build, so it is given priority to connect 
215
     *                                to the database.	
216
     */
217
    public void planRelease(final boolean lastBuildActive) throws SQLException, Exception
218
    {
7048 dpurdie 219
        mLogger.warn("planRelease mDaemon {}", mDaemon);
6914 dpurdie 220
 
221
        mBuildCollection.clear();
222
        mPackageCollection.clear();
7048 dpurdie 223
        mPackageCollectionRipple.clear();
224
        mPackageCollectionTest.clear();
225
        mPackageCollectionWip.clear();
6914 dpurdie 226
        mBuildOrder.clear();
227
        mEscrowRawData = "";
228
        mEscrowSetup = "";
229
        Phase phase = new Phase("Plan");
230
 
231
        // use finally block in planRelease to ensure the connection is released
232
        try
233
        {
234
            phase.setPhase("connectForPlanning");
235
            mReleaseManager.connectForPlanning(lastBuildActive);
236
 
237
            if ( mDaemon )
238
            {
239
                // claim the mutex
240
                mLogger.warn("planRelease claimMutex");
241
                phase.setPhase("claimMutex");
242
                mReleaseManager.claimMutex();
243
 
244
                // Populate the mBuildExclusionCollection
245
                //
246
                // Builds are either 'Directly excluded' or 'Indirectly excluded'
247
                // Direct excludes result from:
248
                //      User request
249
                //      Build failure
250
                //      Inability to build package
251
                // Indirectly excluded packages result from have a build dependency on a package
252
                // that is directly excluded.
253
                //  
254
                // In the following code we will extract from the Database all build exclusions
255
                // We will then add 'Relevant' entries to mBuildExclusionCollection
256
                // and delete, from the database those that are no longer relevant - ie indirectly excluded
257
                // items where the root cause is no longer in the set.
258
                phase.setPhase("mBuildExclusionCollection");
259
                mBuildExclusionCollection.clear();
260
                ArrayList<BuildExclusion> tempBuildExclusionCollection = new ArrayList<BuildExclusion>();
261
 
262
                mLogger.debug("planRelease queryBuildExclusions");
263
                mReleaseManager.queryBuildExclusions(tempBuildExclusionCollection, mBaseline);
264
 
265
                // only populate mBuildExclusionCollection with tempBuildExclusionCollection entries which have a relevant root_pv_id
266
                // The entry is relevant if:
267
                //     It is for a directly excluded package,other than a RippleStop
268
                //     It is for an indirectly excluded package AND the reason for exclusion still exists
269
                //
270
 
271
                for (Iterator<BuildExclusion> it = tempBuildExclusionCollection.iterator(); it.hasNext(); )
272
                {
273
                    BuildExclusion buildExclusion = it.next();
274
 
275
                    if ( buildExclusion.isRelevant(tempBuildExclusionCollection) )
276
                    {
277
                        mBuildExclusionCollection.add(buildExclusion);
278
                    }
279
                    else
280
                    {
281
                        // Remove the indirectly excluded entry as its root cause
282
                        // is no longer present.
283
                        buildExclusion.includeToBuild(mReleaseManager, mBaseline);
284
                    }
285
                }
286
            }
287
 
288
            //-----------------------------------------------------------------------
289
            //	Query package versions
290
            //
291
            phase.setPhase("queryPackageVersions");
292
            mLogger.debug("planRelease queryPackageVersions");
293
            mReleaseManager.queryPackageVersions(this, mPackageCollection, mBaseline);
7048 dpurdie 294
            mReleaseManager.queryWips(this, mPackageCollectionWip, mBaseline);
295
            mReleaseManager.queryTest(this, mPackageCollectionTest, mBaseline);
296
            mReleaseManager.queryRipples(this, mPackageCollectionRipple, mBaseline);
297
            mPackageCollectionAll.addAll(mPackageCollection);
298
            mPackageCollectionAll.addAll(mPackageCollectionWip);
299
            mPackageCollectionAll.addAll(mPackageCollectionTest);
300
            mPackageCollectionAll.addAll(mPackageCollectionRipple);
301
 
302
            // Sort the collection by PVID
303
            //      Unit Test output order is known
304
            //      May assist in creating repeatable build orders
305
            //
306
            Collections.sort(mPackageCollectionAll, Package.SeqComparator);
6914 dpurdie 307
 
308
            //------------------------------------------------------------------------
309
            //    Process packages collected
310
            //    Determine and tag those we can't build
311
            phase.setPhase("processPackages");
312
            processPackages();
313
 
314
            //-----------------------------------------------------------------------
315
            //    At this point we have tagged all the packages that we cannot build
316
            //    Now we can determine what we are building
317
            phase.setPhase("planBuildOrder");
318
            mLogger.debug("planRelease process Remaining");
319
            planBuildOrder();
320
 
321
            //  Report excluded packages and the build plan
322
            //  This is being done with the MUTEX being held, but the 
323
            //  trade off is the cost of getting a connection.
324
            //
325
            if ( mDaemon )
326
            {
327
                phase.setPhase("Report Change");
328
                reportChange();
329
                phase.setPhase("Report Plan");            
330
                reportPlan();
331
            }
332
 
333
            //
334
            //	Generate the build Files
335
            //
336
            phase.setPhase("generateBuildFiles");
337
            generateBuildFiles();
338
        }
339
        finally
340
        {
341
            mLogger.debug("planRelease finally");
342
            // this block is executed regardless of what happens in the try block
343
            // even if an exception is thrown
344
            // ensure the SELECT FOR UPDATE is released
345
            try
346
            {
347
                if ( mDaemon )
348
                {
349
                    // attempt to release the SELECT FOR UPDATE through a commit
350
                    // a commit must be done in the normal case
351
                    // a commit may as well be done in the Exception case
352
                    // in the case of a SQLException indicating database connectivity has been lost
353
                    // having a go at the commit is superfluous
354
                    // as the SELECT FOR UPDATE will have been released upon disconnection
355
                    phase.setPhase("releaseMutex");
356
                    mReleaseManager.releaseMutex();
357
                }
358
            }
359
            finally
360
            {
361
                // ensure disconnect under all error conditions
362
                phase.setPhase("disconnectForPlanning");
363
                mReleaseManager.disconnectForPlanning(lastBuildActive);
364
            }
365
        }
366
 
7048 dpurdie 367
        mLogger.warn("planRelease mDaemon {} returned", mDaemon);
6914 dpurdie 368
        phase.setPhase("EndPlan");
369
    }
370
 
371
    /** Process packages that have been collected as a part of the plan
372
     * @param phase
373
     * @throws SQLException
374
     * @throws Exception
375
     */
376
    private void processPackages() throws SQLException, Exception {
377
 
378
        // Deal with test builds here as they may impact upon package attributes
7048 dpurdie 379
        //    eg: dependency collection and build standard differences
6914 dpurdie 380
        //    Note: Done before mPackageDependencyCollection is setup
381
        //
382
        if ( mDaemon )
383
        {
7048 dpurdie 384
            // Process test builds - they are in their own collection
385
            for (Iterator<Package> it = mPackageCollectionTest.iterator(); it.hasNext(); )
6914 dpurdie 386
            {
387
                Package p = it.next();
7048 dpurdie 388
                mLogger.info("planRelease package test build {}", p.mAlias);
6914 dpurdie 389
 
7048 dpurdie 390
                //
391
                //    Cannot test build an SDK based package or a Pegged Package
392
                //        Remove the test build request from the database
393
                //        Send a warning email
394
                //
395
                if(p.mIsPegged || p.mIsSdk)
6914 dpurdie 396
                {
7048 dpurdie 397
                    String reason;
398
                    reason = (p.mIsPegged) ? "Pegged" : "SDK Based";
6914 dpurdie 399
 
7048 dpurdie 400
                    mLogger.error("planRelease Daemon Instruction (testBuild) of {} package deleted: {}", reason, p.mAlias);
401
                    mReleaseManager.markDaemonInstCompleted( p.mTestBuildInstruction );
402
                    emailRejectedDaemonInstruction("Cannot 'Test Build' a " + reason + " package",p);
403
                }
6914 dpurdie 404
 
7048 dpurdie 405
                // force patch for test build numbering
406
                p.mDirectlyPlanned = true;
407
                p.mChangeType.setPatch();
408
                p.mRequiresSourceControlInteraction = false;
409
                p.mBuildReason = BuildReason.Test;
410
                p.mIndirectlyPlanned = true;
6914 dpurdie 411
            }
412
        }
413
 
414
        // Set up mPackageDependencyCollection on each package
415
        //    Examine the dependencies by alias and convert this to a 'package' selected from the released package set
7048 dpurdie 416
        mLogger.debug("planRelease setup setPackageDependencyCollection");
417
        for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 418
        {
419
            Package p = it.next();
420
 
421
            for (Iterator<String> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); )
422
            {
423
                String alias = it2.next();
424
                Package dependency = findPackage(alias);
425
                p.mPackageDependencyCollection.add(dependency);
426
            }
7048 dpurdie 427
        }    
6914 dpurdie 428
 
7048 dpurdie 429
        // Detect and deal with circular dependencies
430
        // Examine all packages under consideration
431
        //
6914 dpurdie 432
        mLogger.debug("planRelease deal with circular dependencies");
7048 dpurdie 433
        for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 434
        {
435
            Package p = it.next();
436
 
7078 dpurdie 437
            if ( p.hasCircularDependency( mPackageCollectionAll ) )
6914 dpurdie 438
            {
7048 dpurdie 439
                mLogger.info("planRelease circular dependency detected {}", p.mAlias);
6914 dpurdie 440
 
441
                //  Force this package to be marked as having a circular dependency - even if its been excluded
442
                p.mBuildFile = 0;
443
 
444
                // exclude all dependent packages
445
                // max 50 chars
7071 dpurdie 446
                rippleBuildExclude(p, p.mId, "Package has circular dependency", null, null, true, false);
6914 dpurdie 447
 
448
                // take the package out of the build
449
                p.mBuildFile = -6;
7048 dpurdie 450
                mLogger.info("planRelease set mBuildFile to -6 for package {}", p.mAlias );
6914 dpurdie 451
                standardOut(mCircularDependency, p.mAlias, mCircularDependencyFlag);
452
                mCircularDependencyFlag = false;
453
            }
454
        }
455
 
456
        // Scan for packages with missing dependencies
457
        //    ie: The dependent package is not in the package Collection
458
        //        DEVI 55483 now use the fully built mPackageDependencyCollection (in rippleBuildExclude)
459
        mLogger.debug("planRelease use the fully built mPackageDependencyCollection");
7048 dpurdie 460
        for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 461
        {
462
            Package p = it.next();
463
 
464
            if ( mDaemon )
465
            {
466
                //  Daemon Mode Only
467
                //  Not interested in the dependencies of a pegged package or a package provided from an SDK.
468
                //  Such packages will be deemed to not have missing dependencies
469
                if (p.mIsPegged || p.mIsSdk)
470
                {
471
                    continue;
472
                }
473
            }
474
 
475
            for (Iterator<String> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); )
476
            {
477
                String alias = it2.next();
478
                Package dependency = findPackage(alias);
479
 
480
                if (dependency == ReleaseManager.NULL_PACKAGE)
481
                {
7048 dpurdie 482
                    mLogger.info("planRelease dependency is not in the baseline {}", alias);
6914 dpurdie 483
                    // exclude all dependent packages
484
                    // max 50 chars
7071 dpurdie 485
                    rippleBuildExclude(p, p.mId, "Package build dependency not in the release", null, null, true, true);
6914 dpurdie 486
 
487
                    // take the package out of the build
488
                    p.mBuildFile = -4;
7048 dpurdie 489
                    mLogger.info("planRelease set mBuildFile to -4 for package {}", p.mAlias );
6914 dpurdie 490
                    standardOut(mNotInBaseline, p.mAlias, mNotInBaselineFlag);
491
                    mNotInBaselineFlag = false;
492
                    break;
493
                }
494
            }
495
        }
496
 
7048 dpurdie 497
        // Detect packages with no build standard and exclude them from the build
6914 dpurdie 498
        mLogger.debug("planRelease process packages which are not reproducible");
7048 dpurdie 499
        for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 500
        {
501
            Package p = it.next();
502
 
503
            if (p.mBuildFile == 0)
504
            {
505
                if ( mDaemon )
506
                {
507
                    //  Daemon Mode Only
508
                    //  Not interested in the reproducibility of a pegged package or a package provided from an SDK.
509
                    //  Such packages will be deemed to be reproducible - but out side of this release
510
                    if (p.mIsPegged || p.mIsSdk)
511
                    {
512
                        continue;
513
                    }
514
                }
515
 
7048 dpurdie 516
                // Does the package have a build standard. If not then we can't reproduce it.
517
                // Escrow - Assume the package is provided
518
                // Daemon - Exclude this package, but not its consumers. If the package is available then we can use it.
6914 dpurdie 519
                if (!p.isReproducible())
520
                {
7048 dpurdie 521
                    mLogger.info("planRelease package not reproducible {}" ,p.mName);
6914 dpurdie 522
                    // max 50 chars
7071 dpurdie 523
                    rippleBuildExclude(p, p.mId, "Package has no build environment", null, null, false, true);
6914 dpurdie 524
 
525
                    // package is not reproducible, discard
526
                    p.mBuildFile = -1;
7048 dpurdie 527
                    mLogger.info("planRelease set mBuildFile to -1 for package {}", p.mAlias );
6914 dpurdie 528
                    standardOut(mAnyBuildPlatforms, p.mAlias, mAnyBuildPlatformsFlag);
529
                    mAnyBuildPlatformsFlag = false;
530
                }
531
            }
532
        }
533
 
7048 dpurdie 534
        //    Process packages which are not reproducible on the configured set of build machines.
6914 dpurdie 535
        //
536
        //    Test each package and determine if the package contains a buildStandard that
537
        //    can be processed by one of the machines in the build set
538
        //
539
        //    ie: Package: Win32:Production and we have a BM with a class of 'Win32'
540
        //
541
        //    Only exclude the failing package and not its dependents
542
        //    May be legitimate in the release
543
        //
544
        mLogger.debug("planRelease process packages which are not reproducible2");
7048 dpurdie 545
        for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 546
        {
547
            Package p = it.next();
548
 
549
            if (p.mBuildFile == 0)
550
            {
551
                if ( mDaemon )
552
                {
553
                    //  Daemon Mode Only
554
                    //  Not interested in the reproducibility of a pegged package or a package provided from an SDK.
555
                    //  Such packages will be deemed to be reproducible - but out side of this release
556
                    if (p.mIsPegged || p.mIsSdk)
557
                    {
558
                        continue;
559
                    }
560
                }
561
 
562
                // package has yet to be processed
563
                // assume it does not need to be reproduced for this baseline
564
                //
565
                //    For each machineClass in the buildset
566
                boolean reproduce = false;
567
 
568
                for (Iterator<String> it2 = mReleaseManager.mReleaseConfigCollection.mMachineClasses.iterator(); it2.hasNext(); )
569
                {
570
                    String machineClass = it2.next();
571
 
572
                    if ( p.canBeBuildby(machineClass))
573
                    {
574
                        reproduce = true;
7048 dpurdie 575
                        mLogger.info("planRelease package built on {} {}", machineClass, p.mAlias );
6914 dpurdie 576
                        break;
577
                    }
578
                }
579
 
580
                if ( !reproduce )
581
                {
7048 dpurdie 582
                    mLogger.info("planRelease package not reproducible on the build platforms configured for this baseline {}", p.mName);
6914 dpurdie 583
 
7048 dpurdie 584
                    // max 50 chars
7071 dpurdie 585
                    rippleBuildExclude(p, p.mId, "Package not built for configured platforms", null, null, false, true);
6914 dpurdie 586
 
587
                    // package is not reproducible on the build platforms configured for this baseline, discard
588
                    p.mBuildFile = -2;
7048 dpurdie 589
                    mLogger.info("planRelease set mBuildFile to -2 for package {}", p.mAlias );
6914 dpurdie 590
                    standardOut(mAssocBuildPlatforms, p.mAlias, mAssocBuildPlatformsFlag);
591
                    mAssocBuildPlatformsFlag = false;
592
                }
593
            }
594
        }      
595
 
596
        if (mDaemon)
597
        {
598
            // Daemon Mode Only
599
            // Process packages which are not ripple buildable, and all packages dependent upon them
7071 dpurdie 600
            //      I would have thought that all consumer packages had already been excluded ??
6914 dpurdie 601
            mLogger.debug("planRelease process packages which are not ripple buildable");
602
            for (ListIterator<BuildExclusion> it = mBuildExclusionCollection.listIterator(); it.hasNext(); )
603
            {
604
                BuildExclusion be = it.next();
605
 
7071 dpurdie 606
                for (Iterator<Package> it1 = mPackageCollectionAll.iterator(); it1.hasNext(); )
6914 dpurdie 607
                {
608
                    Package p = it1.next();
609
 
610
                    // ensure only root cause, non test build, build exclusions are excluded
611
                    // mBuildExclusionCollection is at this point based on
612
                    // relevant (direct and indirect) excluded pv's in the database
613
                    if ( be.compare(p.mId) && be.isARootCause() && p.mTestBuildInstruction == 0 && p.mForcedRippleInstruction == 0 )
614
                    {
615
                        // package is not reproducible, discard
7048 dpurdie 616
 
7071 dpurdie 617
                        rippleBuildExclude( p, p.mId, null, it, be, true, true );
6914 dpurdie 618
                        p.mBuildFile = -3;
7048 dpurdie 619
                        mLogger.info("planRelease set mBuildFile to -3 for package {}", p.mAlias );
6914 dpurdie 620
                        break;
621
                    }
622
                }
623
            }
624
 
625
            //  Daemon Mode Only
626
            //  Process packages which need to be ripple built
627
            mLogger.debug("planRelease process packages which need to be ripple built");
628
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
629
            {
630
                Package p = it.next();
631
 
632
                if (p.mBuildFile == 0)
633
                {
634
                    //  Not interested in a pegged package or a package provided from an SDK.
635
                    //  Such packages are not rippled
636
                    if (p.mIsPegged || p.mIsSdk)
637
                    {
638
                        continue;
639
                    }
640
 
7048 dpurdie 641
                    //    Examine this packages dependencies
642
                    //    If one of them does not exist in the 'official release' set then
643
                    //    the package needs to be built against the official release set
644
                    //    and we can't build any of its dependent packages
645
                    Iterator<Integer> it2 = p.mDependencyIDCollection.iterator();
646
                    Iterator<Package> it3 = p.mPackageDependencyCollection.iterator();
647
                    while ( it2.hasNext() && it3.hasNext() )
6914 dpurdie 648
                    {
7048 dpurdie 649
                        Integer dpvId = it2.next();
650
                        Package dependency = it3.next();
651
 
652
                        if ( !dependency.mAdvisoryRipple )
6914 dpurdie 653
                        {
7048 dpurdie 654
                            // not advisory, ie: has ripple build impact
655
                            if ( !isInRelease(dpvId) )
6914 dpurdie 656
                            {
7048 dpurdie 657
                                // the package is out of date
658
                                // exclude all dependent package versions
659
                                mLogger.info("planRelease package out of date {}", p.mName);
660
                                p.mBuildReason = BuildReason.Ripple;
661
                                rippleIndirectlyPlanned(p);
662
 
663
                                //  This package needs to be rippled
664
                                //  If this package has a rippleStop marker of 's', then we cannot
665
                                //  build this package at the moment.
666
                                //  Packages that depend on this package have been excluded
667
                                //  We need to exclude this one too
668
                                //
669
                                if (p.mRippleStop == 's' || p.mRippleStop == 'w') 
6914 dpurdie 670
                                {
7048 dpurdie 671
                                    // Package marked as a rippleStop
672
                                    // max 50 chars
7071 dpurdie 673
                                    rippleBuildExclude(p, -2, "Ripple Required." + " Waiting for user", null, null, false, true);
7048 dpurdie 674
 
675
                                    // package is unBuildable, mark reason and discard
676
                                    p.mBuildFile = -11;
6914 dpurdie 677
 
7048 dpurdie 678
                                    if (p.mRippleStop == 's' ) {
679
                                        // Need to flag to users that the package build is waiting user action
680
                                        mLogger.info("planRelease Ripple Required. Stopped by flag {}", p.mName);
681
                                        mReleaseManager.setRippleStopWait(mRtagId,p);
6914 dpurdie 682
                                    }
683
                                }
7048 dpurdie 684
 
685
                                break;
6914 dpurdie 686
                            }
687
                        }
688
                    }
689
                }
690
            }
691
 
692
            //  Daemon Mode Only
693
            //  Process packages which do not exist in the archive
694
            //  For unit test purposes, assume all packages exist in the archive if released
695
            mLogger.debug("planRelease process packages which do not exist in the archive");
7048 dpurdie 696
            if ( mReleaseManager.mUseDatabase )
6914 dpurdie 697
            {
698
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
699
                {
700
                    Package p = it.next();
701
 
702
                    if (p.mBuildFile == 0)
703
                    {
704
                        // package has yet to be processed
705
                        if (!p.mDirectlyPlanned && !p.mIndirectlyPlanned && p.mForcedRippleInstruction == 0)
706
                        {
707
                            // check package version archive existence
708
                            if (!p.existsInDpkgArchive())
709
                            {
710
                                if (! p.mIsBuildable)
711
                                {
712
                                    //  Package does not exist in dpkg_archive and it has been flagged as unbuildable
713
                                    //  This may be because is Unbuildable or Manually built
7048 dpurdie 714
                                    mLogger.info("planRelease Unbuildable package not found in archive {}", p.mName);
6914 dpurdie 715
                                    // max 50 chars
7071 dpurdie 716
                                    rippleBuildExclude(p, p.mId, "Unbuildable" + " package not found in archive", null, null, true, true);
6914 dpurdie 717
 
718
                                    // package is unBuildable, mark reason and discard
719
                                    p.mBuildFile = -10;
720
 
721
                                }
722
                                //  Not interested in a pegged package or a package provided from an SDK.
723
                                //  Such packages are not rippled
724
                                else if (p.mIsPegged || p.mIsSdk)
725
                                {
726
                                    String reason;
727
                                    reason = (p.mIsPegged) ? "Pegged" : "SDK";
728
 
729
                                    //  Pegged packages or packages provided from an SDK MUST exist in dpkg_archive
730
                                    //  They will not be built within the context of this release. It is the responsibility
731
                                    //  of another release to build them.
7048 dpurdie 732
                                    mLogger.info("planRelease {} package not found in archive {}", reason, p.mName);
6914 dpurdie 733
                                    // max 50 chars
7071 dpurdie 734
                                    rippleBuildExclude(p, p.mId, reason + " package not found in archive", null, null, true, true);
6914 dpurdie 735
 
736
                                    // package is not reproducible, mark reason and discard
737
                                    p.mBuildFile = -7;
738
 
739
                                }
740
                                else if (p.mForcedRippleInstruction == 0)
741
                                {
742
                                    //  [JATS-331] Unable to rebuild package with Advisory Ripple dependencies
743
                                    //    Examine this packages dependencies
744
                                    //    If one of them does not exist in the 'official release' set then
745
                                    //    the package cannot be rebuilt in this release.
746
                                    for (Iterator<Integer> it2 = p.mDependencyIDCollection.iterator() ; it2.hasNext() ;)
747
                                    {
748
                                        Integer dpvId = it2.next();
749
                                        if ( !isInRelease(dpvId) )
750
                                        {
751
                                            // This package cannot be rebuilt as one of its dependents is NOT in this release
752
                                            // exclude all dependent package versions
753
 
7048 dpurdie 754
                                            mLogger.info("planRelease package not found in archive. Cannot be rebuilt due to {}", p.mName);
6914 dpurdie 755
                                            // max 50 chars
7071 dpurdie 756
                                            rippleBuildExclude(p, p.mId, "Package cannot be rebuilt in this release", null, null, true, true);
6914 dpurdie 757
 
758
                                            // package is not reproducible, mark reason and discard
759
                                            p.mBuildFile = -4;
760
                                            break;
761
                                        }
762
                                    }
763
 
764
                                    //  The package has not been excluded from the build
765
                                    if (p.mBuildFile == 0)
766
                                    {
7048 dpurdie 767
                                        mLogger.info("planRelease package not found in archive {}", p.mName);
6914 dpurdie 768
                                        // DEVI 47395 the cause of this build is not WIP or ripple induced,
769
                                        // it simply does not exist in the archive (has been removed)
770
                                        // prevent source control interaction
771
                                        p.mRequiresSourceControlInteraction = false;
772
                                        p.mBuildReason = BuildReason.Restore;
773
                                        rippleIndirectlyPlanned(p);
774
                                    }
775
                                }
776
                            }
777
                        }
778
                    }
779
                }
780
            }
781
 
782
            //  Daemon Mode Only
7048 dpurdie 783
            //  Detect bad forced ripples requests and reject them
6914 dpurdie 784
            mLogger.debug("planRelease process forced ripples");
7048 dpurdie 785
            for (Iterator<Package> it = mPackageCollectionRipple.iterator(); it.hasNext(); )
6914 dpurdie 786
            {
787
                Package p = it.next();
788
 
789
                if (p.mBuildFile == 0)
790
                {
7048 dpurdie 791
                    //
792
                    //    Cannot force a ripple on an SDK based package or a Pegged Package
793
                    //        Remove the daemon instruction from the database
794
                    //        Send a warning email
795
                    //
796
                    if(p.mIsPegged || p.mIsSdk)
6914 dpurdie 797
                    {
7048 dpurdie 798
                        String reason;
799
                        reason = (p.mIsPegged) ? "Pegged" : "SDK Based";
6914 dpurdie 800
 
7048 dpurdie 801
                        mLogger.error("planRelease Daemon Instruction of {} package deleted: {}", reason, p.mName);
802
                        mReleaseManager.markDaemonInstCompleted( p.mForcedRippleInstruction );
803
                        emailRejectedDaemonInstruction("Cannot 'Ripple' a " + reason + " package",p);
6914 dpurdie 804
 
7048 dpurdie 805
                        p.mBuildFile = -8; 
6914 dpurdie 806
                    }
807
                }
808
            }
809
 
810
            //  Daemon Mode Only
811
            //  Mark Pegged and SDK packages as not to be built
7078 dpurdie 812
            //  Mark RippleStoped packages as not to be build
6914 dpurdie 813
            //  Have previously detected conflicts between pegged/sdk packages and daemon instructions
814
            //
815
            mLogger.debug("planRelease remove pegged and SDK packages from the build set");
816
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
817
            {
818
                Package p = it.next();
819
 
820
                if (p.mBuildFile == 0)
821
                {
7078 dpurdie 822
                    String reason = null;
823
 
824
                    //  Not interested in a pegged package or a package provided from an SDK or packages with an active RippleStop
6914 dpurdie 825
                    //  Such packages are not built
7078 dpurdie 826
                    if (p.mIsPegged ) {
827
                        reason = "Pegged";
828
                        p.mBuildFile = -8;
829
                    } else if (p.mIsSdk) {
830
                        reason = "SDK";
831
                        p.mBuildFile = -8;
832
                    } else if (p.mRippleStop == 's' || p.mRippleStop == 'w') {
833
                        reason = "RippleStop";
834
                        p.mBuildFile = -11;
835
                    }
836
 
837
                    if (reason != null)
6914 dpurdie 838
                    {
7048 dpurdie 839
                        mLogger.info("planRelease {} not built in this release {}", reason, p.mName);
6914 dpurdie 840
                    }
841
                }
842
            }
843
 
844
            //  Daemon Mode Only
845
            //  Locate Test Build Requests that cannot be satisfied - will not be built due to
846
            //  errors in dependent packages. Report the error to the user and remove the request
847
            //
7048 dpurdie 848
            for (Iterator<Package> it = mPackageCollectionTest.iterator(); it.hasNext(); )
6914 dpurdie 849
            {
850
                Package p = it.next();
851
 
7048 dpurdie 852
                if (p.mBuildFile < 0)
6914 dpurdie 853
                {
7048 dpurdie 854
                    String reason;
855
                    switch (p.mBuildFile)
6914 dpurdie 856
                    {
7048 dpurdie 857
                    case -1:  reason = "Not reproducible"; break;
858
                    case -2:  reason = "Not reproducible on configured build platforms"; break;
859
                    case -3:  reason = "Marked as 'Do not ripple'"; break;
860
                    case -4:  reason = "Dependent on a package not in the release"; break;
861
                    case -5:  reason = "Indirectly dependent on a package not reproducible in the release"; break;
862
                    case -6:  reason = "Has a circular dependency"; break;
863
                    case -7:  reason = "Pegged or SDK package not in dpkg_archive"; break;
864
                    case -8:  reason = "Is a Pegged or SDK package"; break;
865
                    case -9:  reason = "Rejected Daemon Instruction"; break;
866
                    case -10: reason = "Unbuildable package not in dpkg_archive"; break;
867
                    case -11: reason = "Marked as 'RippleStop'"; break;
868
                    default:  reason = "Unknown reason. Code:" + p.mBuildFile; break;
6914 dpurdie 869
                    }
7048 dpurdie 870
                    mLogger.error("planRelease Test Build of an unbuildable of package deleted: {}", p.mName);
871
                    mReleaseManager.markDaemonInstCompleted( p.mTestBuildInstruction );
872
                    emailRejectedDaemonInstruction(reason,p);
6914 dpurdie 873
                }
874
            }
7048 dpurdie 875
 
7078 dpurdie 876
            //  Daemon Mode Only
877
            //  Examine the build candidates and verify that a new version number can be calculated for each
878
            //  version that we need to build.
879
            //
880
            //  If we can't generate a new version number, then this is considered to be a build failure
881
            //  The package will be excluded and the user will be emailed
882
            //
883
            ArrayList<Package> testVersions = new ArrayList<Package>();
884
            testVersions.addAll(mPackageCollectionWip);
885
            testVersions.addAll(mPackageCollectionRipple);
886
 
887
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
888
            {
889
                Package p = it.next();
890
                if (p.mBuildFile >= 0 &&  p.mBuildReason != null) {
891
                    testVersions.add(p);
892
                }
893
            }
894
 
895
            for (Iterator<Package> it = testVersions.iterator(); it.hasNext(); )
896
            {
897
                Package p = it.next();
898
 
899
                if ( p.mBuildFile >= 0 )
900
                {
901
                    int pvApplied = p.applyPV(mReleaseManager);
902
 
903
                    if ( pvApplied == 1 )
904
                    {
905
                        // max 50 chars
906
                        rippleBuildExclude(p, p.mId, "Package has non standard versioning", null, null, true, true);
907
                    }
908
                    else if ( pvApplied == 2 )
909
                    {
910
                        // max 50 chars
911
                        rippleBuildExclude(p, p.mId, "Package has reached ripple field limitations", null, null, true, true);
912
                    }
913
                    else if ( pvApplied == 3 )
914
                    {
915
                        // max 50 chars
916
                        rippleBuildExclude(p, p.mId, "Package has invalid change type", null, null, true, true);
917
                    }
918
                }
919
            }
920
 
6914 dpurdie 921
        }
922
        else
923
        {
924
            // escrow reporting only
925
            // Report packages that are not reproducible
926
            //
927
            //  Note: I don't believe this code can be executed
928
            //        The -3 is only set in daemon mode
929
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
930
            {
931
                Package p = it.next();
932
 
933
                if (p.mBuildFile == -3)
934
                {
935
                    standardOut(mDependent, p.mAlias, mDependentFlag);
936
                    mDependentFlag = false;
937
                }
938
            }
939
        }
940
    }
941
 
7078 dpurdie 942
    /** Internal class to contain intermediate results
6914 dpurdie 943
     */
7078 dpurdie 944
    class PlanResults {
945
        int planTime = 0;
946
        ArrayList<Package> planCollection = new ArrayList<Package>(); 
947
    }
948
 
949
    /**
950
     * Process a collection of packages and generate a collection of package plans
951
     * A package plan is a collection of packages that can be built
952
     *      The first item in the list is a package that can be built right now
953
     *      Other items in the list will be packages that can be built now or will need to be built as the result
954
     *      of a ripple
955
     *      
956
     * The method will be called multiple times so that we can evaluate different plans
957
     * The underling packages will have data and flags that will need to be rested before a calculation      
958
     *
959
     * @param name - Name of the plan
960
     * @param packageCollection - Base collection of packages to plan
961
     * @param mode - true: Include 'Ripples'
962
     * 
963
     * @return
964
     */
965
    private PlanResults planCollection(String name, ArrayList<Package> packageCollection, boolean mode)
7070 dpurdie 966
    {
7078 dpurdie 967
        ArrayList<Package> ripplePlan = new ArrayList<Package>();
968
        PlanResults results = new PlanResults();
6914 dpurdie 969
 
7078 dpurdie 970
        //  Reset flags used in the calculations
971
        Package.resetProcessed(packageCollection);
6914 dpurdie 972
 
7078 dpurdie 973
        //  Exclude all packages that cannot be built and all packages that depend on them
974
        //      First find packages to be directly excluded
975
        ArrayList<Package>exclude = new ArrayList<Package>();
976
        for (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); )
977
        {
978
            Package p = it.next();
979
            if (p.mBuildFile < 0) {
980
                exclude.add(p);
981
            }
982
        }
983
 
984
        //  Process the list of packages to be excluded
985
        //  Add to the list packages that depend on the excluded package that have not already been excluded
986
        while( !exclude.isEmpty() )
987
        {
988
            Package p = exclude.remove(0);
989
            p.mProcessed = true;
990
            p.mIsProcessed = true;
991
            mLogger.info("planCollection package not buildable {}", p.mName);
992
 
993
            for (Iterator<Package> it1 = packageCollection.iterator(); it1.hasNext(); )
994
            {
995
                Package pkg = it1.next();
996
                if (pkg.mProcessed) {
997
                    continue;
998
                }
999
 
1000
                for (Iterator<String> it = p.mDependencyCollection.iterator(); it.hasNext(); )
1001
                {
1002
                    String alias = it.next();
1003
                    if ( p.mAlias.compareTo( alias ) == 0 ) {
1004
                        exclude.add(pkg);
1005
                    }
1006
                }
1007
            }
1008
        }
1009
 
1010
        //  Now have a collection of packages with those that we cannot build have been marked
1011
        //  Process the list and determine packages that we can build right now
1012
        //      ie: they have not been excluded, have not already been discovered and do not depend on any package in the collection
1013
        //      Continue to do this until all packages have been extracted
1014
        //      Allocate build levels as we go
1015
        //
6914 dpurdie 1016
        boolean allProcessed = false;
7078 dpurdie 1017
        int buildLevel = 1;
6914 dpurdie 1018
 
1019
        do
1020
        {
7048 dpurdie 1021
            //
1022
            //  Create the mBuildOrder collection
1023
            //      Same for both Daemon and Escrow modes
1024
            //      Will have package build order and build level
6914 dpurdie 1025
            boolean allDependenciesProcessed = true;
1026
            do
1027
            {
1028
                allDependenciesProcessed = true;
7078 dpurdie 1029
                for (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); )
6914 dpurdie 1030
                {
1031
                    Package p = it.next();
7078 dpurdie 1032
 
1033
                    //  Skip packages that have been processed - have determined build order, or cannot build
1034
                    if (p.mIsProcessed) {
1035
                        continue;
1036
                    }
6914 dpurdie 1037
 
7078 dpurdie 1038
                    // Package yet to be processed and
1039
                    //      Daemon mode: Could be built
1040
                    //      Escrow mode: Can be reproduced
1041
                    boolean canBeBuiltNow = true;
1042
                    boolean allDependenciesForThisPackageProcessed = true;
1043
 
1044
                    //  Scan the packages dependencies
1045
                    for ( Iterator<Package> it2 = p.mPackageDependencyCollection.iterator(); it2.hasNext(); )
6914 dpurdie 1046
                    {
7078 dpurdie 1047
                        Package dependency = it2.next();
6914 dpurdie 1048
 
7078 dpurdie 1049
                        if ( !dependency.mProcessed )
6914 dpurdie 1050
                        {
7078 dpurdie 1051
                            //  If all the dependencies have been processed, then we can potentially build this package
1052
                            //  If any of them have not been processed, then cannot calculate canBeBuiltNow until this 
1053
                            //  dependency has been processed
1054
                            allDependenciesForThisPackageProcessed = false;
1055
                            allDependenciesProcessed = false;
6914 dpurdie 1056
                        }
7078 dpurdie 1057
                        else if (  (  mDaemon && (   dependency.mBuildFile == 0 ) ||  (dependency.mBuildFile == buildLevel)) 
1058
                                || ( !mDaemon && ( ( dependency.mBuildFile == 0 ) ||
1059
                                                       ( dependency.mBuildFile == buildLevel &&
1060
                                                       ( !p.haveSameBuildStandards(dependency)  ) ) ) ) )
1061
                        {
1062
                            // Can be built - yet
1063
                            // Daemon mode:
1064
                            //    This processed dependency has not been assigned to a build iteration
1065
                            //    This processed dependency has been assigned to this build iteration
1066
                            // Escrow mode: This package cannot be built now if
1067
                            //    This processed dependency has not been assigned to a build iteration or
1068
                            //    This processed dependency has been assigned to this build iteration, but the 
1069
                            //    build standards of the package and this dependency prevent the package being
1070
                            //    built in this iteration.   
1071
                            canBeBuiltNow = false;
1072
                            mLogger.info("planRelease package cannot be built in this iteration {}", p.mName);
1073
                            break;
1074
                        }
1075
                    }
6914 dpurdie 1076
 
7078 dpurdie 1077
                    //  All dependencies have been processed. May be able to build this package
1078
                    //      Add the package to the build order list
1079
                    //
1080
                    if (allDependenciesForThisPackageProcessed)
1081
                    {
1082
                        p.mProcessed = true;
1083
                        if ( canBeBuiltNow )
6914 dpurdie 1084
                        {
7078 dpurdie 1085
                            results.planCollection.add(p);
1086
                            p.mBuildFile = buildLevel;
1087
                            p.mIsProcessed = true;
1088
                            mLogger.info("planRelease set mBuildFile to {} for package {}",buildLevel, p.mAlias );
6914 dpurdie 1089
                        }
1090
                    }
1091
                }
1092
            } while( !allDependenciesProcessed );
7048 dpurdie 1093
 
1094
            //  Have process all the packages that we can at the moment
1095
            //      Examine all the packages in the collection to see if there are more that need to be extracted
1096
            //      These will be done at a different build-level
1097
            //  
1098
            allProcessed = true;
7078 dpurdie 1099
            for (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); )
7048 dpurdie 1100
            {
1101
                Package p = it.next();
7078 dpurdie 1102
                if ( !  p.mIsProcessed )
7048 dpurdie 1103
                {
1104
                    // more build files are required
1105
                    allProcessed = false;
1106
                    mLogger.info("planRelease more build files are required for {}", p.mName);
1107
                    break;
1108
                }
1109
            }
6914 dpurdie 1110
 
7078 dpurdie 1111
            buildLevel++;
1112
            if (buildLevel % 500 == 0 ) {
1113
                mLogger.error("planRelease. Too many build levels {}", buildLevel);
7048 dpurdie 1114
            }
1115
        } while( !allProcessed );
7078 dpurdie 1116
 
1117
        //  Now we have a collection packages that we can build right now
1118
        //      The packages currently have a build level set into mBuildFile
1119
        //
1120
        if (mDaemon)
7048 dpurdie 1121
        {
7078 dpurdie 1122
            //  Determine if we have a reason to build anything in this collection of buildable packages
1123
            ArrayList<Package> toBuild = new ArrayList<Package>();
1124
            for (Iterator<Package> it = results.planCollection.iterator(); it.hasNext(); )
6914 dpurdie 1125
            {
7048 dpurdie 1126
                Package p = it.next();
7078 dpurdie 1127
                if (p.mBuildReason != null) {
1128
                    if (mode || ( !mode && p.mBuildReason != BuildReason.Ripple ))
7048 dpurdie 1129
                    {
7078 dpurdie 1130
                        toBuild.add(p);
7048 dpurdie 1131
                    }
1132
                }
1133
            }
1134
 
7078 dpurdie 1135
            //  Need (would like to) build stuff
1136
            //  Determine the packages that we need to build and the time that it will take
7070 dpurdie 1137
            //
7078 dpurdie 1138
            if ( !toBuild.isEmpty() )
7048 dpurdie 1139
            {
7078 dpurdie 1140
                results.planTime = 0;
1141
 
1142
 
1143
                //  Need to add elements to the end of the list while processing
1144
                //  Sum the buildTimes of the packages that we add to the list
1145
                while ( ! toBuild.isEmpty())
7048 dpurdie 1146
                {
7078 dpurdie 1147
                    Package pkg = toBuild.remove(0);
1148
 
1149
                    if (pkg.mRippleOrder > 100 ) {
1150
                        mLogger.error("Circular dependency detected - not handled well");
1151
                        break;
7048 dpurdie 1152
                    }
1153
 
7078 dpurdie 1154
                    if ( ! ripplePlan.contains( pkg ))
7048 dpurdie 1155
                    {
7078 dpurdie 1156
                        ripplePlan.add(pkg);
1157
                        results.planTime += pkg.mBuildTime;
7048 dpurdie 1158
                    }
1159
 
7078 dpurdie 1160
                    ArrayList<Package> usedBy = usedByPackages(pkg, results.planCollection, pkg.mRippleOrder + 1);
1161
                    toBuild.addAll(usedBy);
6914 dpurdie 1162
                }
7078 dpurdie 1163
                //  Sort the packages by buildOrder and buildTime
1164
                Collections.sort(ripplePlan, Package.BuildOrderComparitor);
1165
 
1166
                mLogger.error("Plan Build {} Time: {}", name, results.planTime);
1167
                for (Iterator<Package> it = ripplePlan.iterator(); it.hasNext(); )
1168
                {
1169
                    Package p = it.next();
1170
                    mLogger.error("Plan: {} {}", p.mRippleOrder, p.mAlias);
1171
                }
6914 dpurdie 1172
            }
7048 dpurdie 1173
 
7078 dpurdie 1174
            //  Daemon mode: Returning calculated plan
1175
            results.planCollection = ripplePlan;
1176
        }
1177
 
1178
        return results;
1179
    }
6914 dpurdie 1180
 
7078 dpurdie 1181
    /** Generate a plan based on a modified set of packages
1182
     * 
1183
     * @param name      - Name of the plan
1184
     * @param basicPlan - basicPlan
1185
     * @param mode      - True: Include natural ripples and WIPS/RIPPLES that would have been done
1186
     *                          as a part of the basic plan
1187
     * @return
1188
     */
1189
    private PlanResults postRipplePlan(String name, PlanResults basicPlan, boolean mode) {
1190
 
1191
        ArrayList<Package> fullPlanCollection = new ArrayList<Package>(mPackageCollection);
1192
        ArrayList<Package> buildCandidates = new ArrayList<Package>();
1193
        buildCandidates.addAll(mPackageCollectionWip);
1194
        buildCandidates.addAll(mPackageCollectionRipple);
1195
 
1196
        if ( !buildCandidates.isEmpty() )
1197
        {
7048 dpurdie 1198
            for (Iterator<Package> it = buildCandidates.iterator(); it.hasNext(); )
1199
            {
1200
                Package p = it.next();
7078 dpurdie 1201
                if (p.mBuildFile >= 0)
6914 dpurdie 1202
                {
7078 dpurdie 1203
                    if (!mode)
6914 dpurdie 1204
                    {
7078 dpurdie 1205
                        //  Exclude packages that would have been processed in the basicPlan
1206
                        for (Iterator<Package> it1 = basicPlan.planCollection.iterator(); it1.hasNext(); )
7048 dpurdie 1207
                        {
7078 dpurdie 1208
                            Package pkg = it1.next();
1209
                            if (pkg == p)
1210
                            {
1211
                                mLogger.error("Test Plan without {}", p.mAlias);
1212
                                continue;
1213
                            }
7048 dpurdie 1214
                        }
6914 dpurdie 1215
                    }
7078 dpurdie 1216
 
1217
                    mLogger.error("Test Plan with {}", p.mAlias);
1218
                    Package foundInRelease = mReleaseManager.findPackage(p.mAlias, fullPlanCollection);
1219
                    int foundIndex = mReleaseManager.findPackageLastIndex;
1220
                    if (foundInRelease == ReleaseManager.NULL_PACKAGE)
1221
                    {
1222
                        fullPlanCollection.add(p);
1223
                    }
1224
                    else
1225
                    {
1226
                        fullPlanCollection.set(foundIndex, p);
1227
                    }
1228
 
1229
                    Package.resetCircularDependency (fullPlanCollection);
1230
                    if ( p.hasCircularDependency( fullPlanCollection ) )
1231
                    {
1232
                        mLogger.info("planRelease circular dependency detected {}", p.mAlias);
1233
 
1234
                        //  Force this package to be marked as having a circular dependency - even if its been excluded
1235
                        p.mBuildFile = 0;
1236
 
1237
                        // exclude all dependent packages
1238
                        // max 50 chars
1239
                        rippleBuildExclude(p, p.mId, "Package has circular dependency", null, null, true, false);
1240
 
1241
                        // take the package out of the build
1242
                        p.mBuildFile = -6;
1243
                        mLogger.info("planRelease set mBuildFile to -6 for package {}", p.mAlias );
1244
                    }
6914 dpurdie 1245
                }
1246
            }
7048 dpurdie 1247
 
7078 dpurdie 1248
        }
1249
        return planCollection(name, fullPlanCollection, mode);
1250
    }
1251
 
1252
    /** Plan the build order.
1253
     *  Assumes that a great deal of work has been done.
1254
     *  This is a stand alone method to contain the work
1255
     *  
1256
     * @throws Exception
1257
     * @throws SQLException
1258
     */
1259
    private void planBuildOrder() throws Exception, SQLException 
1260
    {
1261
 
1262
//TODO - Planning is not working well
1263
/**
1264
 * Current status
1265
 * The selection of the package to build next is deeply flawed
1266
 * Currently it will select a directly planned package over a ripple - (perhaps that is good)
1267
 * 
1268
 * Currently on the buildPlan of the 'selected' package is included in the BuildOrder
1269
 * 
1270
 * The current algorithm is based on time - but all packages have zero build time ( in UTF )
1271
 * Even if this is fixed the algorithm is broken as we will select the complete build with the shortest time
1272
 * which will be the ripple of a leaf package ( perhaps that is good )
1273
 * 
1274
 * Also:
1275
 *      [Done] Need to include all packages that we can now build into the final build plan, not just the first one and its build tree
1276
 * 
1277
 *      [Done-ish]Need to generate a single package set - so that WIPS and others replace those in the final build set
1278
 *      [Done]Remove duplicates from the complete package set
1279
 *      
1280
 *      [Being Done] Need more test cases
1281
 *      
1282
 *      [Done, in the UTF] Need to report all build options, not just the selected one
1283
 *                         Data to RM may need some more info
1284
 *      
1285
 *      Cleanup the ANT build file - don't list all the release dependencies, just those needed to build the current package
1286
 *      Chances are - all we need is the new package version number
1287
 *      
1288
 *      [Fixed] If a WIP on a package that has circular dependencies is added, then the WIP will be excluded. This prevents the problem being fixed.
1289
 *              Should not exclude a WIP of a package with a circular dependency
1290
 */
1291
 
1292
        // Process remaining packages which are need to be reproduced for this baseline.
1293
        //    Determine the build file for each package
1294
        //    For daemon builds:
1295
        //      Determine the next package that can be built now
1296
        //          Sounds simple - doesn't it
1297
        //      Set its mBuildNumber to 1, all remaining reproducible packages to 2
1298
        //    For escrow builds:
1299
        //      Determine the package versions that can be built in the build iteration
1300
        //      Set their mBuildNumber to the build iteration
1301
        //      Increment the build iteration and repeat until all package versions 
1302
        //      that need to be reproduced have been assigned a build iteration        
1303
 
1304
        //
1305
        //  Generate a plan for the packages that are in the current release
1306
        //  These may be the result of a ripple going through the system 
1307
        //  or a rebuild of a missing package or the result of a merge.
1308
        //
1309
        //  This will provide our basic plan, before we consider adding new packages to the
1310
        //  mix, such as those from user WIPS and RIPPLE requests.
1311
        //  
1312
        PlanResults basicPlan = planCollection("Basic", mPackageCollection, true);
1313
 
1314
        //  Have not considered any of the WIP or RIPPLE packages
1315
        //  If any of them are in the basic plan, then we can ignore them as we will build them
1316
        //  when we build the basic plan
1317
        //
1318
        //  Determine a set of packages that will not be included if we build the basic plan
1319
        //
1320
        //  At the moment - lets assume there are none
1321
        //  What do we do ?
1322
        //      Select the first request
1323
        //      Replace the package in the released collection
1324
        //      Run the plan algorithm
1325
        //      Restore the release collection
1326
        //
1327
        if ( mDaemon )
1328
        {
7048 dpurdie 1329
            //
7078 dpurdie 1330
            //  Need to consider the TEST requests
1331
            //      - Valid requests will be placed first on the build order
7048 dpurdie 1332
            //
7078 dpurdie 1333
            mBuildOrder.clear();
1334
            for (Iterator<Package> it = mPackageCollectionTest.iterator(); it.hasNext(); )
6914 dpurdie 1335
            {
7070 dpurdie 1336
                Package p = it.next();
7078 dpurdie 1337
                if (p.mBuildFile >= 0)
7070 dpurdie 1338
                {
7078 dpurdie 1339
                    mBuildOrder.add(p);
1340
                    p.mRippleOrder = 0;
7070 dpurdie 1341
                }
1342
            }
1343
 
7078 dpurdie 1344
            //  First attempt
1345
            //  Insert all the WIPs and RIPPLES into the buildset. 
1346
            //  Generate a plan and see how much the time is extended.
1347
            //
1348
            //  Don't modify mPackageCollection, although data in the 'Package' in the underlying packages may change
1349
 
1350
            PlanResults fullPlan = null;           
1351
            fullPlan = postRipplePlan("Full", basicPlan, true);
1352
 
1353
            //  Decide with plan to use
1354
            //  At the moment we have, at most two.
1355
            //      - (basic) Current Release ripples
1356
            //      - (full) Current Release + All WIPS and RIPPLES
1357
            //
1358
            //  If we have both then if the full plan is < 20% longer than the basic plan, use the full plan
1359
            //  Otherwise we use basic plan FOLLOWED by the a MODIFIED full plan (one without the natural ripples)
1360
            //  Add the planned packages into the buildOrder
1361
 
1362
            if( !basicPlan.planCollection.isEmpty() && fullPlan != null )
7070 dpurdie 1363
            {
7078 dpurdie 1364
                mLogger.error("Two plan selection: {} {}, {} <= {}", basicPlan.planTime,fullPlan.planTime, basicPlan.planTime * 12, fullPlan.planTime * 10);
1365
                if ( (basicPlan.planTime * 12) <= (fullPlan.planTime * 10) )
7070 dpurdie 1366
                {
7078 dpurdie 1367
                    //  Use the basic plan FOLLOWED by a plan that does not include
1368
                    //      Ripples done by the basic plan
1369
                    //      WIPs/RIPPLES done in the course of doing the basic plan
7070 dpurdie 1370
 
7078 dpurdie 1371
                    mBuildOrder.addAll(basicPlan.planCollection);
1372
                    fullPlan = postRipplePlan("Full-Ripples", basicPlan, false);
1373
                    mBuildOrder.addAll(fullPlan.planCollection);
1374
 
1375
                } else {
1376
                    //  Use the full plan
1377
                    mBuildOrder.addAll(fullPlan.planCollection);
7070 dpurdie 1378
                }
7078 dpurdie 1379
            } else if (!basicPlan.planCollection.isEmpty() ) {
1380
                // Use the basic plan
1381
                mBuildOrder.addAll(basicPlan.planCollection);
1382
            } else if ( fullPlan != null ) {
1383
                // Use the full plan
1384
                mBuildOrder.addAll(fullPlan.planCollection);
1385
            } else {
1386
                // Do not have a plan
1387
                // May have tests requests
7048 dpurdie 1388
            }
1389
 
7078 dpurdie 1390
            //  Now have an mBuildOrder
7048 dpurdie 1391
            //  Mark the first package in the build order as the one to be built
1392
            //
7078 dpurdie 1393
            Package build = ReleaseManager.NULL_PACKAGE;
7048 dpurdie 1394
            if (!mBuildOrder.isEmpty()) {
1395
                build = mBuildOrder.get(0);
1396
                build.mBuildFile = 1;
1397
 
1398
                if ( build.mForcedRippleInstruction > 0 )
6914 dpurdie 1399
                {
7048 dpurdie 1400
                    mReleaseManager.markDaemonInstCompleted( build.mForcedRippleInstruction );
1401
                }
6914 dpurdie 1402
 
7048 dpurdie 1403
                if ( build.mTestBuildInstruction > 0 )
1404
                {
1405
                    mReleaseManager.markDaemonInstInProgress( build.mTestBuildInstruction );
1406
                }
1407
 
1408
                //  Now that we know which package we are building
1409
                //      Set the previously calculated nextVersion as the packages version number
7071 dpurdie 1410
                //      Claim the version number to prevent other builds from using it. Even if doing a test build
7048 dpurdie 1411
                //
7071 dpurdie 1412
                if (build.mNextVersion != null)
7048 dpurdie 1413
                {
1414
                    mReleaseManager.claimVersion(build.mPid, build.mNextVersion + build.mExtension, mBaseline);
1415
                    build.mVersion = build.mNextVersion;
1416
                }
1417
            }
1418
 
1419
            //
7070 dpurdie 1420
            //  Massage the package collection
1421
            //      Replace WIP/TEST/RIPPLE so that the mPackageCollection is a collection of packages we would like in the release
1422
            //      Don't replace those that we can't build due to errors
7078 dpurdie 1423
            //  
1424
            //  TODO - Not sure this does anything for us. Once upon a time it might have been nice, but now it may be confusing
7070 dpurdie 1425
            //
7078 dpurdie 1426
            ArrayList<Package> buildCandidates = new ArrayList<Package>();
1427
            buildCandidates.addAll(mPackageCollectionTest);
1428
            buildCandidates.addAll(mPackageCollectionWip);
1429
            buildCandidates.addAll(mPackageCollectionRipple);
1430
 
7070 dpurdie 1431
            for (Iterator<Package> it = buildCandidates.iterator(); it.hasNext(); )
1432
            {
1433
                Package p = it.next();
1434
                if ( p.mBuildFile >= 0 )
1435
                {
1436
                    Package pReleased = findPackage(p.mAlias);
1437
                    if ( pReleased != ReleaseManager.NULL_PACKAGE)
1438
                    {
1439
                        int index = mReleaseManager.findPackageLastIndex;
1440
                        mPackageCollection.set(index, p);
1441
                        p.mIsNotReleased = false;
1442
                    }
1443
                }
1444
            }
1445
 
1446
 
1447
            //
7048 dpurdie 1448
            //  To fit in with the old algorithm ( ie: could be improved )
1449
            //  Insert marks into all packages
1450
            //  Not sure exactly why - Its used in the generation of the ant build file
7070 dpurdie 1451
            //                     Want to set mNoBuildReason, mBuildFile
7048 dpurdie 1452
            //
1453
            //      Package we have selected to build: 0     , 1
7070 dpurdie 1454
            //      Package we could have built      : 0     , 2
7048 dpurdie 1455
            //      Packages we can't build          : reason, 3
1456
            //      Packages that are OK             : 3     , 3
7070 dpurdie 1457
            //      ????                             : 0     , 3
7048 dpurdie 1458
 
1459
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
1460
            {
1461
                Package p = it.next();
1462
                if (p == build ) {
1463
                    p.mNoBuildReason = 0;
1464
                    p.mBuildFile = 1;
1465
                } 
1466
                else if ( p.mBuildFile < 0 )
1467
                {
1468
                    p.mNoBuildReason = p.mBuildFile;
1469
                    p.mBuildFile = 3;
1470
                }
1471
                else if (p.mBuildReason != null)
1472
                {
7070 dpurdie 1473
                    p.mNoBuildReason = 0;
1474
                    p.mBuildFile = 2;
7048 dpurdie 1475
                }
1476
                else
1477
                {
1478
                    p.mNoBuildReason = 0;
1479
                    p.mBuildFile = 3;
1480
                }
1481
            }
7070 dpurdie 1482
 
7048 dpurdie 1483
        }
7078 dpurdie 1484
        else
7070 dpurdie 1485
        {
7078 dpurdie 1486
            //  Escrow
1487
            //  The basic plan is the escrow build order
1488
            mBuildOrder = basicPlan.planCollection;
7070 dpurdie 1489
        }
1490
 
7078 dpurdie 1491
        mLogger.error("Final Plan");
1492
        for (Iterator<Package> it = mBuildOrder.iterator(); it.hasNext(); )
7048 dpurdie 1493
        {
7078 dpurdie 1494
            Package p = it.next();
1495
            mLogger.error("Plan: {} {}", p.mRippleOrder, p.mAlias);
7048 dpurdie 1496
        }
7078 dpurdie 1497
 
1498
 
7048 dpurdie 1499
    }
1500
 
1501
    /** 
1502
     *  Calculate a collection of packages that actively use the named package
1503
     *  A consumer package does NOT use the named package,
1504
     *      If the consumer is an SDK or is Pegged
1505
     *      If the consumer is marked as advisoryRipple
1506
     *      If the consumer cannot be built
1507
     *   
1508
     * @param pkg - Package to process
7070 dpurdie 1509
     * @param pkgCollection - collection of packages to scan
7071 dpurdie 1510
     * @param rippleOrder - Set the ripple oder, if >= 0
7078 dpurdie 1511
     *
7048 dpurdie 1512
     * @return A collection of packages that actively 'use' the specified package
1513
 
1514
     */
7071 dpurdie 1515
    private ArrayList<Package> usedByPackages(Package pkg, ArrayList<Package> pkgCollection, int rippleOrder) {
7048 dpurdie 1516
 
1517
        ArrayList<Package> usedBy = new ArrayList<Package>();
1518
 
7070 dpurdie 1519
        for (Iterator<Package> it = pkgCollection.iterator(); it.hasNext(); )
7048 dpurdie 1520
        {
1521
            Package p = it.next();
1522
 
1523
            //  Is this package 'actively used' in the current build
1524
            if (p.mBuildFile >= 0)
1525
            {
1526
                for (Iterator<String> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); )
1527
                {
1528
                    String alias = it2.next();
7070 dpurdie 1529
                    if (  pkg.mAlias.compareTo( alias ) == 0  ) {
7048 dpurdie 1530
                        //  Have found a consumer of 'pkg'
1531
                        usedBy.add(p);
7071 dpurdie 1532
                        if (rippleOrder >= 0)
1533
                            p.mRippleOrder = rippleOrder;
6914 dpurdie 1534
                        break;
1535
                    }
1536
                }
1537
            }
7048 dpurdie 1538
        }
1539
 
1540
        return usedBy;
6914 dpurdie 1541
    }
1542
 
1543
    /** Determine if a given PVID is a member of the current release.
1544
     *  Used to determine if a package dependency is out of date
1545
     * 
1546
     * @param dpvId
1547
     * @return true - specified pvid is a full member of the Release
1548
     */
1549
    private boolean isInRelease(Integer dpvId) {
1550
 
7048 dpurdie 1551
        boolean inRelease = false;
6914 dpurdie 1552
 
7048 dpurdie 1553
        for ( Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
6914 dpurdie 1554
        {
7048 dpurdie 1555
            Package p = it.next();
6914 dpurdie 1556
 
7048 dpurdie 1557
            if ( p.mId == dpvId )
6914 dpurdie 1558
            {
7048 dpurdie 1559
                inRelease = ! p.mIsNotReleased;
6914 dpurdie 1560
                break;
1561
            }
1562
        }
7048 dpurdie 1563
        return inRelease;
6914 dpurdie 1564
    }
1565
 
1566
 
1567
    /**reports what change in build exceptions happens as part of planRelease
1568
     */
1569
    public void reportChange() throws SQLException, Exception
1570
    {
1571
        int counter = 0;
1572
        for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1573
        {
1574
            BuildExclusion buildExclusion = it.next();
1575
 
1576
            if ( !buildExclusion.isProcessed() )
1577
            {
7032 dpurdie 1578
                if (buildExclusion.isImported() && ! buildExclusion.isARootCause() ) {
1579
                    // Remove from the exclusion list
1580
                    buildExclusion.includeToBuild(mReleaseManager, mBaseline);
7048 dpurdie 1581
                    mLogger.error("reportChange remove unused exclusion: {}", buildExclusion.info());
7032 dpurdie 1582
                } else {
1583
                    // Exclude and notify
1584
                    buildExclusion.excludeFromBuild(mReleaseManager, mBaseline);
7071 dpurdie 1585
                    buildExclusion.email(this, mPackageCollectionAll);
7032 dpurdie 1586
                    counter++;
1587
                }
6914 dpurdie 1588
            }
1589
        }
7048 dpurdie 1590
        mLogger.error("reportChange exclusion count: {}", counter);
6914 dpurdie 1591
    }
1592
 
1593
    /**reports the build plan
1594
     */
1595
    public void reportPlan() throws SQLException, Exception
1596
    {
1597
        mReleaseManager.reportPlan(mRtagId, mBuildOrder);
1598
    }
1599
 
1600
    /**Returns the first build file from the collection
1601
     * The build file will be flagged as empty if none exists
1602
     */
1603
    public BuildFile getFirstBuildFileContent()
1604
    {
1605
        mLogger.debug("getFirstBuildFileContent");
1606
 
1607
        mBuildIndex = -1;
1608
        return getNextBuildFileContent();
1609
    }
1610
 
1611
    /**Returns next build file from the collection
1612
     * The build file will be flagged as empty if none exists
1613
     */
1614
    public BuildFile getNextBuildFileContent()
1615
    {
1616
        mLogger.debug("getNextBuildFileContent");
1617
        BuildFile retVal = null;
1618
 
1619
        try
1620
        {
1621
            mBuildIndex++;
1622
            retVal = mBuildCollection.get( mBuildIndex );
1623
        }
1624
        catch( IndexOutOfBoundsException e )
1625
        {
1626
            // Ignore exception. retVal is still null.
1627
        }
1628
 
1629
        if (retVal == null)
1630
        {
1631
            retVal = new BuildFile();
1632
        }
1633
 
7048 dpurdie 1634
        mLogger.debug("getNextBuildFileContent returned {}", retVal.state.name() );
6914 dpurdie 1635
        return retVal;
1636
    }
1637
 
1638
 
1639
    /**collects meta data associated with the baseline
1640
     * this is sufficient to send an indefinite pause email notification 
1641
     *  
1642
     * Escrow: Used once to collect information about the SBOM and associated Release 
1643
     *         mBaseline is an SBOMID 
1644
     * Daemon: Used each build cycle to refresh the information 
1645
     *          mBaseline is an RTAGID
1646
     */
1647
    public void collectMetaData() throws SQLException, Exception
1648
    {
1649
        Phase phase = new Phase("cmd");
7048 dpurdie 1650
        mLogger.debug("collectMetaData mDaemon {}", mDaemon);
6914 dpurdie 1651
 
1652
        try
1653
        {
1654
            phase.setPhase("connect");
1655
            mReleaseManager.connect();
1656
 
1657
            if (! mDaemon)
1658
            {
1659
                mSbomId = mBaseline;
1660
                phase.setPhase("queryRtagIdForSbom");
1661
                mRtagId = mReleaseManager.queryRtagIdForSbom(mBaseline);
1662
                if (mRtagId == 0)
1663
                {
7033 dpurdie 1664
                    mLogger.error("SBOM does not have a matching Release. Cannot be used as a base for an Escrow"); 
6914 dpurdie 1665
                    throw new Exception("rtagIdForSbom show stopper. SBOM does not have an associated Release");
1666
                }
1667
            }
1668
 
1669
            phase.setPhase("queryReleaseConfig");
1670
            mReleaseManager.queryReleaseConfig(mRtagId);
1671
 
1672
            if (mDaemon)
1673
            {
1674
                phase.setPhase("queryMailServer");
1675
                setMailServer(mReleaseManager.queryMailServer());
1676
                phase.setPhase("queryMailSender");
1677
                setMailSender(mReleaseManager.queryMailSender());
1678
                phase.setPhase("queryGlobalAddresses");
1679
                setMailGlobalTarget(mReleaseManager.queryGlobalAddresses());
1680
                phase.setPhase("queryProjectEmail");
1681
                mMailGlobalCollection = mReleaseManager.queryProjectEmail(mBaseline);
1682
                phase.setPhase("mMailGlobalTarget");
1683
                mMailGlobalCollection.add(0,getMailGlobalTarget());
1684
            }
1685
            phase.setPhase("queryBaselineName");
1686
            mBaselineName = mReleaseManager.queryBaselineName(mBaseline);
1687
            phase.setPhase("Done");
1688
        }
1689
        finally
1690
        {
1691
            // this block is executed regardless of what happens in the try block
1692
            // even if an exception is thrown
1693
            // ensure disconnect
1694
            mReleaseManager.disconnect();
1695
        }
1696
    }
1697
 
1698
    /**
1699
     * Find Package by package alias
7070 dpurdie 1700
     * Searches the released package collection
6914 dpurdie 1701
     * @param   alias               - alias of package to locate
1702
     * @return  Package with the matching mAlias or NULL_PACKAGE if no package has the mAlias
1703
     */
1704
    public Package findPackage(String alias)
1705
    {
1706
        mLogger.debug("findPackage");
1707
 
1708
        Package retVal = mReleaseManager.findPackage(alias, mPackageCollection);
1709
 
7048 dpurdie 1710
        mLogger.info("findPackage returned {}", retVal.mName);
6914 dpurdie 1711
        return retVal;
1712
    }
7070 dpurdie 1713
 
6914 dpurdie 1714
    /**
1715
     * Sets the mBuildFile to -5 (indirectly dependent on package versions which are not reproducible) 
7048 dpurdie 1716
     * for the package and all dependent packages that are a part of the release set.
1717
     *  
7032 dpurdie 1718
     * @param p             The package being excluded 
1719
     * @param rootPvId      The PVID of the package that is causing the exclusion. Null or -ve values are special
1720
     *                      This package is the root cause, -2: Excluded by Ripple Stop 
1721
     * @param rootCause     Text message. Max 50 characters imposed by RM database 
1722
     * @param list          If not null, add build exclusion to specified list, else use default(mBuildExclusionCollection) list. Allows insertions while iterating the list.
1723
     * @param be            If not null, process provided BuildExclusion element, otherwise find-existing/create a BuildExclusion element
1724
     * @param dependentToo  True: Exclude all packages that depend on the excluded package 
7071 dpurdie 1725
     * @param depWipsToo    False: Don't exclude WIP packages that depend on the excluded package
6914 dpurdie 1726
     */
7071 dpurdie 1727
    private void rippleBuildExclude(Package p, int rootPvId, String rootCause, ListIterator<BuildExclusion> list, BuildExclusion be, boolean dependentToo, boolean depWipsToo )
6914 dpurdie 1728
    {
1729
        mLogger.debug("rippleBuildExclude");
7048 dpurdie 1730
 
6914 dpurdie 1731
        if ( p.mBuildFile == 0 || p.mBuildFile == 1 )
1732
        {
1733
            p.mBuildFile = -5;
7048 dpurdie 1734
            mLogger.info("rippleBuildExclude set mBuildFile to -5 for package {}", p.mAlias );
6914 dpurdie 1735
 
1736
            if ( be != null )
1737
            {
7032 dpurdie 1738
                be.setProcessed();
6914 dpurdie 1739
            }
1740
            else
1741
            {
1742
                //  If no be item has been provided, then scan the complete collection looking for a matching item
1743
                //  If found, process it, else add it (unprocessed)
1744
                boolean found = false;
1745
                for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1746
                {
1747
                    BuildExclusion buildExclusion = it.next();
1748
 
1749
                    if ( buildExclusion.compare(p.mId, rootPvId, rootCause))
1750
                    {
7032 dpurdie 1751
                        buildExclusion.setProcessed();
6914 dpurdie 1752
                        found = true;
1753
                        break;
1754
                    }
1755
                }
1756
 
1757
                if (!found)
1758
                {
1759
                    // Entry not found in the mBuildExclusionCollection
1760
                    // Mark all occurrences for this package as processed
1761
                    // These will be superseded by a new build exclusion entry
1762
                    for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1763
                    {
1764
                        BuildExclusion buildExclusion = it.next();
1765
 
1766
                        if ( buildExclusion.compare(p.mId))
1767
                        {
7032 dpurdie 1768
                            buildExclusion.setProcessed();
6914 dpurdie 1769
                        }
1770
                    }
1771
 
1772
                    BuildExclusion buildExclusion = new BuildExclusion(p.mId, rootPvId, rootCause, p.mTestBuildInstruction);
1773
 
1774
                    //
1775
                    //  Add the new build exclusion to a list
1776
                    //    Either the default list or a user-specified list
1777
                    if ( list == null )
1778
                    {
1779
                        mBuildExclusionCollection.add(buildExclusion);
1780
                    }
1781
                    else
1782
                    {
1783
                        // must use the ListIterator interface to add to the collection whilst iterating through it
1784
                        list.add(buildExclusion);
1785
                    }
1786
                }
1787
            }
1788
 
1789
            //  Locate ALL packages that depend on this package and exclude them too
1790
            //  This process will be recursive
1791
            //    Not sure that this it is efficient 
7048 dpurdie 1792
            //  Only ripple a build exclusion through for packages that are a part of the full release set
1793
            //  NewWIPs, ForcedRipples and TestBuilds will not force consumer packages to be excluded
6914 dpurdie 1794
            //
7048 dpurdie 1795
            if ( ! p.mIsNotReleased && dependentToo)
6914 dpurdie 1796
            {
7048 dpurdie 1797
                for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 1798
                {
1799
                    Package pkg = it.next();
7071 dpurdie 1800
 
1801
                    //  Skip myself
1802
                    if (pkg == p)
1803
                        continue;
6914 dpurdie 1804
 
7071 dpurdie 1805
                    //  Ignore dependent WIPS if required
1806
                    if (!depWipsToo && pkg.mDirectlyPlanned)
1807
                        continue;
1808
 
1809
                    for (Iterator<Package> it2 = pkg.mPackageDependencyCollection.iterator(); it2.hasNext(); )
6914 dpurdie 1810
                    {
7071 dpurdie 1811
                        Package dependency = it2.next();
1812
 
1813
                        if ( dependency == p )
6914 dpurdie 1814
                        {
7071 dpurdie 1815
                            rippleBuildExclude( pkg, rootPvId, null, list, null, dependentToo, depWipsToo );
1816
                            break;
6914 dpurdie 1817
                        }
1818
                    }
7071 dpurdie 1819
 
6914 dpurdie 1820
                }
1821
            }
1822
        }
7048 dpurdie 1823
        mLogger.info("rippleBuildExclude set {} {}", p.mName, p.mBuildFile);
6914 dpurdie 1824
    }
1825
 
1826
    /**Simple XML string escaping
1827
     * 
1828
     * @param xml		- String to escape
1829
     * @return		- A copy of the string with XML-unfriendly characters escaped 
1830
     */
1831
    public static String escapeXml( String xml )
1832
    {
1833
        xml = xml.replaceAll("&", "&amp;");
1834
        xml = xml.replaceAll("<", "&lt;");
1835
        xml = xml.replaceAll(">", "&gt;");
1836
        xml = xml.replaceAll("\"","&quot;");
1837
        xml = xml.replaceAll("'", "&apos;");
1838
        xml = xml.replaceAll("\\$", "\\$\\$");
1839
 
1840
        return xml;
1841
    }
1842
 
1843
    /** Quote a string or a string pair
1844
     *  If two strings are provided, then they will be joined with a comma.
1845
     *   
1846
     * @param text		- First string to quote
1847
     * @param text2		- Optional, second string
1848
     * @return A string of the form 'text','text2'
1849
     */
1850
    public static String quoteString(String text, String text2)
1851
    {
1852
        String result;
1853
        result =  "\'" + text + "\'";
1854
        if (text2 != null )
1855
        {
1856
            result +=  ",\'" + text2 + "\'";  
1857
        }
1858
        return result;
1859
    }
1860
 
1861
    /** Generate build file information
1862
     * 
1863
     */
1864
    private void generateBuildFiles() 
1865
    {
1866
 
1867
        // persist the build files
1868
        boolean allProcessed = false;
1869
        int buildFile = 1;
1870
        StringBuilder rawData = new StringBuilder();
1871
        StringBuilder setUp = new StringBuilder();
1872
 
1873
        mLogger.debug("generateBuildFiles");
1874
 
1875
        if ( mDaemon )
1876
        {
1877
            // all interesting packages in daemon mode match the following filter
1878
            buildFile = 3;
1879
        }
1880
 
1881
        //-----------------------------------------------------------------------
1882
        //    Generate the build file
1883
        do
1884
        {
1885
            BuildFile buildEntry = new  BuildFile();
1886
            buildEntry.state = BuildFileState.Dummy;
1887
            XmlBuilder xml = generateBuildFileHeader();
1888
 
1889
 
1890
            //	Generate properties for each package in this build level or lower build levels
1891
            //	The properties link the packageAlias to the PackageName and PackageVersion
1892
            //
1893
            //	[DEVI 54816] In escrow mode all unreproducible packages are included 
7070 dpurdie 1894
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
6914 dpurdie 1895
            {
1896
                Package p = it.next();
1897
 
1898
                if ( ( ( p.mBuildFile > 0 ) && ( p.mBuildFile <= buildFile ) ) || ( !mDaemon && p.mBuildFile == -2 ) )
1899
                {
1900
                    xml.addProperty(p.mAlias, p.mName + " " + p.mVersion + p.mExtension);
1901
                }
1902
            }
1903
 
1904
            //	UTF Support
1905
            //	Insert additional info into the build file to provide extra checking
1906
            //
7048 dpurdie 1907
            if ( ! mReleaseManager.mUseDatabase )
6914 dpurdie 1908
            {
1909
                // UTF Support
1910
                // Insert per-package planning information
1911
                //
7070 dpurdie 1912
                xml.addComment("mPackageCollection");
1913
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
6914 dpurdie 1914
                {
1915
                    Package p = it.next();
1916
                    generatePackageInfo(xml, p);
1917
                }
1918
 
7070 dpurdie 1919
                xml.addComment("mPackageCollectionWip");
1920
                for (Iterator<Package> it = mPackageCollectionWip.iterator(); it.hasNext(); )
1921
                {
1922
                    Package p = it.next();
1923
                    if (p.mIsNotReleased )
1924
                        generatePackageInfo(xml, p);
1925
                }
1926
 
1927
                xml.addComment("mPackageCollectionTest");
1928
                for (Iterator<Package> it = mPackageCollectionTest.iterator(); it.hasNext(); )
1929
                {
1930
                    Package p = it.next();
1931
                    if (p.mIsNotReleased )
1932
                        generatePackageInfo(xml, p);
1933
                }
1934
 
1935
                xml.addComment("mPackageCollectionTestRipple");
1936
                for (Iterator<Package> it = mPackageCollectionRipple.iterator(); it.hasNext(); )
1937
                {
1938
                    Package p = it.next();
1939
                    if (p.mIsNotReleased )
1940
                        generatePackageInfo(xml, p);
1941
                }
1942
 
6914 dpurdie 1943
                // UTF Support
1944
                // Insert build exclusion information
1945
                xml.addComment("mBuildExclusionCollection");
1946
                for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1947
                {
1948
                    BuildExclusion buildExclusion = it.next();
1949
                    if (!buildExclusion.isProcessed())
1950
                    {
1951
                        xml.addComment(buildExclusion.info());
1952
                    }
1953
                }
1954
 
1955
                // UTF Support
1956
                // Insert build Plan
1957
                if (mDaemon)
1958
                {
1959
                    xml.addComment("mBuildOrder");
1960
                    int order = 0;
1961
                    for (Iterator<Package> it = mBuildOrder.iterator(); it.hasNext(); )
1962
                    {
1963
                        Package p = it.next();
1964
                        String comment =
1965
                                "pvid="+ p.mId +
7048 dpurdie 1966
                                " order=" + (++order) +
7070 dpurdie 1967
                                " Rorder=" + (p.mRippleOrder) +
7078 dpurdie 1968
                                " time=" + p.mBuildTime +
6914 dpurdie 1969
                                " name=\"" + p.mAlias + "\"";
1970
                        xml.addComment(comment);
1971
                    }
1972
                }
1973
            }
1974
 
1975
            //  Generate Taskdef information
1976
            generateTaskdef(xml);
1977
 
1978
            //
1979
            //  Insert known Machine Information
1980
            //  Escrow usage: 
1981
            //      Map machType to machClass
1982
            //  Also serves as a snapshot of the required build configuration
1983
            //  ie: machine types and buildfilters
1984
            //
1985
            if (!mDaemon)
1986
            {
1987
                generateMachineInfo(xml, null);
1988
            }
1989
 
1990
            //
1991
            //	Generate target rules for each package to be built within the current build file
1992
            //
1993
            boolean daemonHasTarget = false;
1994
 
7048 dpurdie 1995
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 1996
            {
1997
                Package p = it.next();
1998
 
1999
                if ( p.mBuildFile > 0 && p.mBuildFile <= buildFile )
2000
                {
2001
                    generateTarget(xml, buildEntry, p, buildFile);
2002
 
2003
                    if ( p.mBuildFile == 1 )
2004
                    {
2005
                        daemonHasTarget = true;
2006
 
2007
                        // Retain information about the target package
2008
                        buildEntry.mPkgId = p.mPid;
2009
                        buildEntry.mPvId = p.mId;
2010
                    }
2011
                }
2012
 
2013
                //	Generate escrow extraction commands
2014
                //	Only done on the first pass though the packages
2015
                //
2016
                if ( !mDaemon && buildFile == 1 )
2017
                {
2018
                    setUp.append("jats jats_vcsrelease -extractfiles"
2019
                                + " \"-label=" + p.mVcsTag + "\""
2020
                                + " \"-view=" + p.mAlias + "\""
2021
                                + " -root=. -noprefix"
2022
                                + mlf );
2023
                }
2024
 
2025
                //	Generate escrow raw CSV data
2026
                //	Note: I don't think this data is used at all
2027
 
2028
                if ( !mDaemon && ( p.mBuildFile == buildFile))
2029
                {
2030
                    StringAppender machines = new StringAppender(",");
2031
                    for (Iterator<BuildStandard> it1 = p.mBuildStandardCollection.iterator(); it1.hasNext();)
2032
                    {
2033
                        BuildStandard bs = it1.next();
2034
                        machines.append(bs.mMachClass);
2035
                    }
2036
 
2037
                    rawData.append(p.mAlias + "," +
2038
                                    buildFile + "," +
2039
                                    machines +
2040
                                    mlf);
2041
                }
2042
            }
2043
 
2044
            if ( mDaemon && !daemonHasTarget )
2045
            {
2046
                // must have AbtTestPath, AbtSetUp, AbtTearDown, and AbtPublish targets
2047
                XmlBuilder target = xml.addNewElement("target");
2048
                target.addAttribute("name", "AbtTestPath");
2049
 
2050
                target = xml.addNewElement("target");
2051
                target.addAttribute("name", "AbtSetUp");
2052
 
2053
                target = xml.addNewElement("target");
2054
                target.addAttribute("name", "AbtTearDown");
2055
 
2056
                target = xml.addNewElement("target");
2057
                target.addAttribute("name", "AbtPublish");
2058
            }
2059
 
2060
            generateDefaultTarget( xml, buildFile);
2061
 
2062
            //	Convert the Xml structure into text and save it in the build file
2063
            //	Add this build file to the mBuildCollection
2064
            buildEntry.content = mXmlHeader + xml.toString();
2065
            mBuildCollection.add(buildEntry);
2066
 
2067
            // are more build files required
2068
            allProcessed = true;
2069
 
2070
            if (!mDaemon)
2071
            {
2072
                // this is escrow mode centric
7048 dpurdie 2073
                for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2074
                {
2075
                    Package p = it.next();
2076
 
2077
                    if ( p.mBuildFile > buildFile )
2078
                    {
2079
                        // more build files are required
2080
                        allProcessed = false;
7048 dpurdie 2081
                        mLogger.info("planRelease reiterating package has no build requirement {} {} {}",p.mName, p.mBuildFile,  buildFile);
6914 dpurdie 2082
                        break;
2083
                    }
2084
                } 
2085
 
2086
                buildFile++;
2087
            }
2088
 
2089
        } while( !allProcessed );
2090
 
2091
        //	Save additional escrow data
2092
        if ( !mDaemon )
2093
        {
2094
            mEscrowSetup = setUp.toString();
2095
            mEscrowRawData = rawData.toString();
2096
        }
2097
    }
2098
 
2099
    /**returns a build file header for the mBaseline
2100
     */
2101
    private XmlBuilder generateBuildFileHeader()
2102
    {
2103
        mLogger.debug("generateBuildFileHeader");
2104
        XmlBuilder element = new XmlBuilder("project");
2105
 
2106
        element.addAttribute("name", "mass");
2107
        element.addAttribute("default", "full");
2108
        element.addAttribute("basedir", ".");
2109
 
2110
        if ( mDaemon )
2111
        {
2112
            element.addProperty("abt_mail_server", getMailServer());
2113
            element.addProperty("abt_mail_sender", getMailSender()); 
2114
            element.addProperty("abt_rtag_id", mBaseline);
2115
            element.addProperty("abt_daemon", mReleaseManager.currentTimeMillis());
2116
            element.makePropertyTag("abt_packagetarball", true);
2117
            element.makePropertyTag("abt_usetestarchive", !ReleaseManager.getUseMutex());
2118
 
7048 dpurdie 2119
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2120
            {
2121
                Package p = it.next();
2122
 
2123
                if ( p.mBuildFile == 1 )
2124
                {
2125
                    element.addProperty("abt_package_name", p.mName);
2126
                    element.addProperty("abt_package_version", p.mVersion + p.mExtension);
2127
                    element.addProperty("abt_package_extension", p.mExtension);
2128
                    element.addProperty("abt_package_location", getBuildLocation(p));
2129
                    element.addProperty("abt_package_ownerlist", p.emailInfoNonAntTask(this));
2130
                    element.addProperty("abt_package_build_info", buildInfoText(p));
2131
 
2132
                    // depends in the form 'cs','25.1.0000.cr';'Dinkumware_STL','1.0.0.cots'
2133
                    StringAppender depends = new StringAppender(";");
2134
 
2135
                    for (Iterator<Package> it3 = p.mPackageDependencyCollection.iterator(); it3.hasNext(); )
2136
                    {
2137
                        Package depend = it3.next();
2138
                        depends.append( quoteString(depend.mName, depend.mVersion + depend.mExtension) );
2139
                    }
2140
 
2141
                    element.addProperty("abt_package_depends", depends.toString());
2142
                    element.addProperty("abt_is_ripple", p.mDirectlyPlanned ? "0" : "1");
2143
                    element.addProperty("abt_build_reason", p.mBuildReason.toString());
2144
                    element.addProperty("abt_package_version_id", p.mId);
2145
                    element.addProperty("abt_does_not_require_source_control_interaction", ! p.mRequiresSourceControlInteraction ? "true" : "false" );
2146
                    element.addProperty("abt_test_build_instruction", p.mTestBuildInstruction);
2147
                }
2148
            }
2149
        }
2150
        else
2151
        {
2152
            //    Escrow Mode
2153
            element.addProperty("abt_rtag_id", mRtagId);
2154
            element.addProperty("abt_sbom_id", mSbomId);
2155
        }
2156
 
2157
        element.addProperty("abt_release", escapeXml(mBaselineName));
2158
        element.addProperty("abt_buildtool_version", mReleaseManager.getMajorVersionNumber() );
2159
 
2160
        element.addNewElement("condition")
2161
        .addAttribute("property", "abt_family")
2162
        .addAttribute("value", "windows")
2163
        .addNewElement("os")
2164
        .addAttribute("family", "windows");
2165
        element.addProperty("abt_family", "unix");
2166
 
2167
        return element;
2168
    }
2169
 
2170
    /** Add task def XML items taskdef for the abt ant task
2171
     * @param xml 	- xml item to extend
2172
     */
2173
    private void generateTaskdef(XmlBuilder xml)
2174
    {
2175
        mLogger.debug("generateTaskdef");
2176
        xml.addNewElement("taskdef")
2177
        .addAttribute("name", "abt")
2178
        .addAttribute("classname", "com.erggroup.buildtool.abt.ABT");
2179
 
2180
        xml.addNewElement("taskdef")
2181
        .addAttribute("name", "abtdata")
2182
        .addAttribute("classname", "com.erggroup.buildtool.abt.ABTData");
2183
    }
2184
 
2185
    /** returns the command abtdata items
2186
     *  <br>Common machine information
2187
     *  <br>Common email address
2188
     *  
2189
     *  
2190
     *  @param	xml - Xml element to extend   
2191
     *  @param    p - Package (May be null)
2192
     */
2193
    private void generateMachineInfo(XmlBuilder xml, Package p)
2194
    {
2195
        XmlBuilder element = xml.addNewElement("abtdata");
2196
        element.addAttribute("id", "global-abt-data");
2197
 
2198
        //
2199
        //  Iterate over all the machines and create a nice entry
2200
        //
2201
        for (Iterator<ReleaseConfig> it = mReleaseManager.mReleaseConfigCollection.mReleaseConfig.iterator(); it.hasNext(); )
2202
        {
2203
            ReleaseConfig rc = it.next();
2204
            element.addElement(rc.getMachineEntry());
2205
        }
2206
 
2207
        //
2208
        //  Now the email information
2209
        //
2210
        if ( p != null)
2211
        {
2212
            p.emailInfo(this, element);
2213
        }
2214
    }
2215
 
2216
    /** Generate package information
2217
     *  Only when running unit tests
2218
     *  
2219
     * @param xml	- An xml element to append data to
2220
     * @param p	- Package to process
2221
     */
2222
    private void generatePackageInfo (XmlBuilder xml, Package p)
2223
    {
7071 dpurdie 2224
        StringBuilder comment = new StringBuilder();
2225
        StringBuilder deps = new StringBuilder();
2226
 
7051 dpurdie 2227
        String joiner = "";
2228
        for (Iterator<String> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); )
2229
        {
2230
            String alias = it2.next();
7071 dpurdie 2231
            deps.append(joiner).append(alias); 
7051 dpurdie 2232
            joiner = ",";
2233
        }
2234
 
7071 dpurdie 2235
        comment.append("pvid=").append(p.mId);
2236
        comment.append(" name=").append('"').append(p.mAlias).append('"');
2237
        comment.append(" reason=").append(p.mNoBuildReason);
2238
        comment.append(" buildFile=").append(p.mBuildFile);
2239
        comment.append(" directlyPlanned=").append(p.mDirectlyPlanned);
2240
        comment.append(" indirectlyPlanned=").append(p.mIndirectlyPlanned);
2241
        comment.append(" depends=[").append(deps).append("]");
6914 dpurdie 2242
 
7071 dpurdie 2243
        xml.addComment(comment.toString());            
6914 dpurdie 2244
    }
2245
 
2246
    /**returns an ant target for the passed Package
2247
     * in daemon mode:
2248
     *  packages are categorized with one of three mBuildFile values:
2249
     *   1 the package to be built by this buildfile
2250
     *   2 the packages with a future build requirement
2251
     *   3 the packages with no build requirement
2252
     *  the returned target depends on this categorization and will have
2253
     *   1 full abt info
2254
     *   2 full dependency info to determine future build ordering but no abt info (will not build this package)
2255
     *   3 only a name attribute (will not build this package)
2256
     * in escrow mode:
2257
     *  if the passed Package's mBuildFile is different (less than) the passed build file,
2258
     *  the returned target have only a name attribute (will not build this package)
2259
     *   
2260
     * @param xml - Xml element to extend
2261
     * @param buildEntry - Record build type (dummy/generic/not generic)
2262
     * @param p - Package to process
2263
     * @param buildFile - buildfile level being processed
2264
     */
2265
    private void generateTarget(XmlBuilder xml, BuildFile buildEntry, Package p, int buildFile)
2266
    {
2267
        mLogger.debug("generateTarget");
2268
 
2269
        //---------------------------------------------------------------------
2270
        //  Generate the AbtData - common machine and email information
2271
        //  Only used by the daemon builds
2272
        //
2273
        if ( mDaemon && p.mBuildFile == 1 )
2274
        {
2275
            generateMachineInfo(xml, p );
2276
        }
2277
 
2278
        //-------------------------------------------------------------------------
2279
        //  Generate the <target name=... /> construct
2280
        //  There are two types
2281
        //      1) Simple dummy place holder. Just has the PackageName.PackageExt
2282
        //      2) Full target. Has all information to build the package including
2283
        //              AbtSetUp, AbtTearDown and AbtPublish targets
2284
        //
2285
 
2286
        if ( !mDaemon && ( p.mBuildFile < buildFile ) )
2287
        {
2288
            XmlBuilder target = xml.addNewElement("target");
2289
            target.addAttribute("name", p.mAlias);
2290
        }
2291
        else
2292
        {
2293
            if (!mDaemon) 
2294
            {
2295
                //  Escrow Only:
2296
                //  Generate the 'wrapper' target
2297
                //  This is used to ensure that the required dependencies have been built - I think
2298
                //
2299
                StringAppender dependList = new StringAppender(",");
2300
                if ( !p.mPackageDependencyCollection.isEmpty() )
2301
                {
2302
                    for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
2303
                    {
2304
                        Package dependency = it.next();
2305
                        if ( !mDaemon && dependency.mBuildFile == -2 )
2306
                        {
2307
                            // ignore targets which build in foreign environments in escrow mode
2308
                            continue;
2309
                        }
2310
                        dependList.append(dependency.mAlias);
2311
                    }
2312
                }
2313
 
2314
                XmlBuilder target = xml.addNewElement("target").isExpanded();
2315
                target.addAttribute("name", p.mAlias + ".wrap");
2316
 
2317
                if (dependList.length() > 0)
2318
                {
2319
                    target.addAttribute("depends", dependList.toString() );
2320
                }
2321
 
2322
                if ( !mDaemon )
2323
                {
2324
                    boolean hasDependenciesBuiltInThisIteration = false;
2325
                    if ( ( !p.mPackageDependencyCollection.isEmpty()) )
2326
                    {
2327
                        for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
2328
                        {
2329
                            Package dependency = it.next();
2330
 
2331
                            if ( dependency.mBuildFile == buildFile )
2332
                            {
2333
                                hasDependenciesBuiltInThisIteration = true;
2334
                                break;
2335
                            }
2336
                        }
2337
                    }
2338
 
2339
                    if ( hasDependenciesBuiltInThisIteration )
2340
                    {
2341
                        XmlBuilder condition = target.addNewElement("condition");
2342
                        condition.addAttribute("property",  p.mAlias + ".build");
2343
 
2344
                        XmlBuilder and = condition.addNewElement("and");
2345
 
2346
                        for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
2347
                        {
2348
                            Package dependency = it.next();
2349
 
2350
                            if ( dependency.mBuildFile == buildFile )
2351
                            {
2352
                                XmlBuilder or = and.addNewElement("or");
2353
 
2354
                                XmlBuilder equal1 = or.addNewElement("equals");
2355
                                equal1.addAttribute("arg1", "${" + dependency.mAlias + ".res}");
2356
                                equal1.addAttribute("arg2", "0");
2357
 
2358
                                XmlBuilder equal2 = or.addNewElement("equals");
2359
                                equal2.addAttribute("arg1", "${" + dependency.mAlias + ".res}");
2360
                                equal2.addAttribute("arg2", "257");
2361
                            }
2362
                        }
2363
                    }
2364
                    else
2365
                    {
2366
                        target.addProperty(p.mAlias + ".build", "");
2367
                    }
2368
                }
2369
            }
2370
 
2371
 
2372
            //
2373
            //  Generate the 'body' of the target package
2374
            //  Escrow Mode: Always
2375
            //  Daemon Mode: Only for the one target we are building
2376
            //                  Simplifies the XML
2377
            //                  Reduces noise in the logs
2378
            //              Don't add target dependencies. 
2379
            //                  We are only building one target and the 
2380
            //                  dependency management has been done way before now.
2381
            //                  All it does is makes the logs noisy.
2382
            //
2383
            if ( ( mDaemon && p.mBuildFile == 1 ) || !mDaemon )
2384
            {
2385
                XmlBuilder target = xml.addNewElement("target").isExpanded();
2386
                target.addAttribute("name", p.mAlias);
2387
 
2388
                if ( !mDaemon )
2389
                {
2390
                    target.addAttribute("depends",  p.mAlias + ".wrap");
2391
                    target.addAttribute("if",p.mAlias + ".build");
2392
                }
2393
 
2394
                if ( mDaemon && p.mBuildFile == 1 )
2395
                {
2396
                    target.addProperty(p.mAlias + "pkg_id",p.mPid);
2397
                    target.addProperty(p.mAlias + "pv_id",p.mId);
2398
                }
2399
 
2400
                target.addProperty(p.mAlias + "packagename",p.mName);        		
2401
                target.addProperty(p.mAlias + "packageversion",p.mVersion);
2402
                target.addProperty(p.mAlias + "packageextension",p.mExtension);
2403
                target.addProperty(p.mAlias + "packagevcstag",p.mVcsTag);
2404
 
2405
                target.makePropertyTag(p.mAlias + "directchange", p.mDirectlyPlanned); 
2406
                target.makePropertyTag(p.mAlias + "doesnotrequiresourcecontrolinteraction", ! p.mRequiresSourceControlInteraction);
2407
 
2408
                buildEntry.state = BuildFile.BuildFileState.NonGeneric;
2409
                if ( p.isGeneric() )
2410
                {
2411
                    buildEntry.state = BuildFile.BuildFileState.Generic;
2412
                    target.makePropertyTag(p.mAlias + "generic", true); 
2413
                }
2414
 
2415
                target.addProperty(p.mAlias + "loc", getBuildLocation(p));
2416
                target.makePropertyTag(p.mAlias + "unittests", p.mHasAutomatedUnitTests && mDaemon);
2417
 
2418
                //    Add our own task and associated information
2419
                //
2420
                XmlBuilder abt = target.addNewElement("abt").isExpanded();
2421
 
2422
                for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
2423
                {
2424
                    Package dependency = it.next();
2425
                    XmlBuilder depend = abt.addNewElement("depend");
2426
                    depend.addAttribute("package_alias", "${" + dependency.mAlias + "}");
2427
                }
2428
 
2429
                buildInfo(abt,p);
2430
 
2431
                if ( mDaemon && p.mBuildFile == 1 )
2432
                {
2433
                    //    AbtTestPath
2434
                    target = xml.addNewElement("target").isExpanded();
2435
                    target.addAttribute("name", "AbtTestPath");
2436
                    target.addProperty("AbtTestPathpackagevcstag", p.mVcsTag);
2437
                    target.addProperty("AbtTestPathpackagename", p.mName);
2438
                    abt = target.addNewElement("abt").isExpanded();
2439
                    buildInfo(abt, p);
2440
 
2441
 
2442
                    //    AbtSetUp
2443
                    target = xml.addNewElement("target").isExpanded();
2444
                    target.addAttribute("name", "AbtSetUp");
2445
                    target.addProperty("AbtSetUppackagevcstag", p.mVcsTag);
2446
                    target.addProperty("AbtSetUppackagename", p.mName);
2447
 
2448
                    abt = target.addNewElement("abt").isExpanded();
2449
                    buildInfo(abt, p);
2450
 
2451
                    //    AbtTearDown
2452
                    target = xml.addNewElement("target").isExpanded();
2453
                    target.addAttribute("name", "AbtTearDown");
2454
                    target.addProperty("AbtTearDownpackagevcstag", p.mVcsTag);
2455
                    target.addProperty("AbtTearDownpackagename", p.mName);
2456
                    target.addProperty("AbtTearDownpackageversion", p.mVersion);
2457
                    target.addProperty("AbtTearDownpackageextension", p.mExtension);
2458
                    target.makePropertyTag(p.mAlias + "generic", p.isGeneric());
2459
 
2460
                    abt = target.addNewElement("abt").isExpanded();
2461
                    buildInfo(abt, p);
2462
 
2463
 
2464
                    //  AbtPublish
2465
                    target = xml.addNewElement("target").isExpanded();
2466
                    target.addAttribute("name", "AbtPublish");
2467
 
2468
                    target.addProperty("AbtPublishpackagevcstag", p.mVcsTag);
2469
                    target.addProperty("AbtPublishpackagename", p.mName);
2470
                    target.addProperty("AbtPublishpackageversion", p.mVersion);
2471
                    target.addProperty("AbtPublishpackageextension", p.mExtension);
2472
                    target.makePropertyTag("AbtPublishdirectchange", p.mDirectlyPlanned);
2473
                    target.makePropertyTag("AbtPublishdoesnotrequiresourcecontrolinteraction", ! p.mRequiresSourceControlInteraction);
2474
                    target.makePropertyTag("AbtPublishgeneric", p.isGeneric());
2475
                    target.addProperty("AbtPublishloc", getBuildLocation(p));
2476
 
2477
                    abt = target.addNewElement("abt").isExpanded();
2478
                    buildInfo(abt, p);
2479
 
2480
                }
2481
            }
2482
        }
2483
    }
2484
 
2485
    /** Extends the xml object. Adds ant default target for the current build iteration
2486
     * @param xml - The XmlBuilder Object to extend
2487
     * @param buildFile - The current build file level. This differs for Daemon and Escrow mode. In Daemon mode it will not be a '1' 
2488
     */
2489
    private void generateDefaultTarget(XmlBuilder xml, int buildFile)
2490
    {
2491
        mLogger.debug("generateDefaultTarget");
2492
 
2493
        XmlBuilder target = xml.addNewElement("target").isExpanded();
2494
        target.addAttribute("name", "fullstart");
2495
 
2496
        if (buildFile == 1)
2497
        {
2498
            antEcho(target, "${line.separator}" + mAnyBuildPlatforms + "${line.separator}${line.separator}");
7048 dpurdie 2499
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2500
            {
2501
                Package p = it.next();
2502
 
2503
                if ( p.mBuildFile == -1 )
2504
                {
2505
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2506
                }
2507
            }
2508
 
2509
            antEcho(target, "${line.separator}" + mAssocBuildPlatforms + "${line.separator}${line.separator}");
7048 dpurdie 2510
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2511
            {
2512
                Package p = it.next();
2513
 
2514
                if ( p.mBuildFile == -2 )
2515
                {
2516
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2517
                }
2518
            }
2519
 
2520
            antEcho(target, "${line.separator}" + mNotInBaseline + "${line.separator}${line.separator}");
7048 dpurdie 2521
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2522
            {
2523
                Package p = it.next();
2524
 
2525
                if ( p.mBuildFile == -4 )
2526
                {
2527
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2528
                }
2529
            }
2530
 
2531
            antEcho(target, "${line.separator}" + mDependent + "${line.separator}${line.separator}");
7048 dpurdie 2532
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2533
            {
2534
                Package p = it.next();
2535
 
2536
                if ( p.mBuildFile == -5 )
2537
                {
2538
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2539
                }
2540
            }
2541
        }
2542
 
2543
        if ( !mDaemon )
2544
        {
2545
            antEcho(target, "${line.separator}Build Started:${line.separator}${line.separator}");
2546
        }
2547
 
2548
        //
2549
        //	Create a comma separated list of all the required targets
2550
        //      Escrow : All packages
2551
        //      Daemon : Just the package being built
2552
        //
2553
        StringAppender dependList = new StringAppender(",");
2554
        dependList.append("fullstart");
7048 dpurdie 2555
        for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2556
        {
2557
            Package p = it.next();
2558
 
2559
            if ( ( p.mBuildFile > 0 ) && ( p.mBuildFile <= buildFile ) )
2560
            {
2561
                if ((mDaemon && p.mBuildFile == 1) || !mDaemon)
2562
                {
2563
                    dependList.append(p.mAlias);                    
2564
                }
2565
            }
2566
        }
2567
 
2568
        target = xml.addNewElement("target").isExpanded();
2569
        target.addAttribute("name", "full");
2570
        target.addAttribute("depends", dependList.toString());
2571
 
2572
        if ( !mDaemon )
2573
        {
2574
            antEcho(target, "${line.separator}Build Finished${line.separator}");
2575
        }
2576
    }
2577
 
2578
    /** Internal helper function to create an ant 'echo statement
2579
     *  Many of the parameters are fixed
2580
     */
2581
    private void antEcho( XmlBuilder xml, String message)
2582
    {
2583
        XmlBuilder msg = xml.addNewElement("echo");
2584
        msg.addAttribute("message", message);
2585
        msg.addAttribute("file", "publish.log");
2586
        msg.addAttribute("append", "true");
2587
    }
2588
 
2589
    /**sets the mIndirectlyPlanned true for the package and all dependent packages
2590
     */
2591
    private void rippleIndirectlyPlanned(Package p)
2592
    {
2593
        mLogger.debug("rippleIndirectlyPlanned");
2594
        if ( !p.mIndirectlyPlanned && p.mBuildFile == 0 )
2595
        {
2596
            p.mIndirectlyPlanned = true;
2597
 
7048 dpurdie 2598
            for (Iterator<Package> it = mPackageCollectionAll.iterator(); it.hasNext(); )
6914 dpurdie 2599
            {
2600
                Package pkg = it.next();
2601
 
2602
                if ( pkg != p )
2603
                {
2604
                    for (Iterator<Package> it2 = pkg.mPackageDependencyCollection.iterator(); it2.hasNext(); )
2605
                    {
2606
                        Package dependency = it2.next();
2607
 
2608
                        if ( dependency == p )
2609
                        {
2610
                            rippleIndirectlyPlanned( pkg );
2611
                            break;
2612
                        }
2613
                    }
2614
                }
2615
            }
2616
        }
7048 dpurdie 2617
        mLogger.info("rippleIndirectlyPlanned set {} {}", p.mName, p.mIndirectlyPlanned);    
6914 dpurdie 2618
    }
2619
 
2620
    /**accessor method
2621
     */
2622
    public String getEscrowSetUp()
2623
    {
2624
        mLogger.debug("getEscrowSetUp");
2625
        String retVal = mEscrowSetup;
2626
 
7048 dpurdie 2627
        mLogger.debug("getEscrowSetUp returned {}", retVal);
6914 dpurdie 2628
        return retVal;
2629
    }
2630
 
2631
    /**accessor method
2632
     */
2633
    public String getRawData()
2634
    {
2635
        mLogger.debug("getRawData");
2636
        String retVal = mEscrowRawData;
2637
 
7048 dpurdie 2638
        mLogger.debug("getRawData returned {}", retVal);
6914 dpurdie 2639
        return retVal;
2640
    }
2641
 
2642
    /**Get the build loc (location)
2643
     * This is package specific and will depend on the build mode (Escrow/Daemon)
2644
     * 
2645
     * @param	p - Package being built
2646
     * @return A string that describes the build location
2647
     */
2648
    private String getBuildLocation(Package p)
2649
    {
2650
        mLogger.debug("locationProperty");
2651
        String location = "";
2652
 
2653
        if (mDaemon)
2654
        {
2655
            // Daemon: Start in root of view/workspace
2656
            location += mBaseline;
2657
        }
2658
        else
2659
        {
2660
            // Escrow: mAlias used with jats -extractfiles -view
2661
            location += p.mAlias;
2662
        }
2663
 
2664
        //
2665
        //  Always use '/' as a path separator - even if user has specified '\'
2666
        //  Ant can handle it.
2667
        //
2668
        location = location.replace('\\', '/');
2669
        return location;
2670
    }
2671
 
2672
    /**Adds package build into as XML elements 
2673
     * @param	  xml 	- Xml element to extend
2674
     * @param   p       - Package to process
2675
     */
2676
    private void buildInfo(XmlBuilder xml, Package p)
2677
    {
2678
        mLogger.debug("buildInfo");
2679
 
2680
        //
2681
        // Create the xml build information
2682
        // <platform gbe_machtype="linux_i386" type="jats" arg="all"/>
2683
        //
2684
        for (Iterator<BuildStandard> it = p.mBuildStandardCollection.iterator(); it.hasNext();)
2685
        {
2686
            BuildStandard bs = it.next();
2687
            bs.getBuildStandardXml(xml);
2688
        }
2689
    }
2690
 
2691
    /**returns the buildInfo as a single line of text
2692
     * Used for reporting purposes only
2693
     * @param   p       - Package to process
2694
     * 
2695
     */
2696
    public String buildInfoText(Package p)
2697
    {
2698
        mLogger.debug("buildInfoText");
2699
 
2700
        StringAppender result = new StringAppender (";");
2701
 
2702
        //
2703
        //  Create platform:standards
2704
        //      
2705
        for (Iterator<BuildStandard> it = p.mBuildStandardCollection.iterator(); it.hasNext(); )
2706
        {
2707
            BuildStandard bs = it.next();
2708
 
2709
            if ( bs.isActive() )
2710
            {
2711
                String info = bs.getBuildStandardText();
2712
                result.append(info);
2713
            }
2714
        }
2715
 
7048 dpurdie 2716
        mLogger.info("buildInfoText returned {}", result);
6914 dpurdie 2717
        return result.toString();
2718
    }
2719
 
2720
    /**prints to standard out in escrow mode only
2721
     * <br>Prints a title and information. The title is only printed once.
2722
     * 
2723
     * @param header - The message title to display, if printMessage is true
2724
     * @param text - A package name. Really just the 2nd line of the message
2725
     * @param printHeader -  Controls the printing of the message argument
2726
     */
2727
    private void standardOut(final String header, final String text, boolean printHeader)
2728
    {
2729
        mLogger.debug("standardOut");
2730
        if (!mDaemon)
2731
        {
2732
            if ( printHeader )
2733
            {
2734
                System.out.println(header);
2735
            }
2736
 
2737
            System.out.println(text);
2738
        }
2739
    }
2740
 
2741
 
2742
    /**
2743
     *  Email users about a rejected daemon instruction
2744
     *  @param  reason  - Reason for the rejection
2745
     *  @param  pkg     - Package to be affected by the instruction
2746
     *  
2747
     */
2748
    public void emailRejectedDaemonInstruction(String reason, Package p)
2749
    {
2750
        mLogger.debug("emailRejectedDaemonInstruction");
2751
 
2752
        //  Email Subject
2753
        String subject = "BUILD FAILURE of Daemon Instruction on package " + p.mAlias;
2754
 
2755
        // Email Body
2756
        String mailBody = "The build system reject the the Daemon Instruction";
2757
        mailBody += "<br>Reason: " + reason; 
2758
 
2759
        mailBody += "<p>Release: " + mBaselineName 
2760
                +  "<br>Package: " + p.mAlias 
2761
                +  "<br>Rm Ref: " + CreateUrls.generateRmUrl(getRtagId(), p.mId); 
2762
 
2763
        mailBody += "<p><hr>";
2764
 
2765
        String target = p.emailInfoNonAntTask(this);
2766
 
7048 dpurdie 2767
        mLogger.error("emailRejectedDaemonInstruction Server: {}", getMailServer());
2768
        mLogger.error("emailRejectedDaemonInstruction Sender: {}", getMailSender());
2769
        mLogger.error("emailRejectedDaemonInstruction Target: {}", target);
6914 dpurdie 2770
 
2771
        try
2772
        {
2773
            //    
7048 dpurdie 2774
            Smtpsend.send(getMailServer(),  // mailServer
2775
                    getMailSender(),        // source
2776
                    target,                 // target
2777
                    getMailSender(),        // cc
2778
                    null,                   // bcc
2779
                    subject,                // subject
2780
                    mailBody,               // body
2781
                    null                    // attachment
6914 dpurdie 2782
                    );
2783
        } catch (Exception e)
2784
        {
7048 dpurdie 2785
            mLogger.warn("Email Failure: emailRejectedDaemonInstruction:{}", e.getMessage());
6914 dpurdie 2786
        }
2787
    }
2788
 
2789
    /**
2790
     * @return the mMailServer
2791
     */
2792
    public String getMailServer() {
2793
        return mMailServer;
2794
    }
2795
 
2796
    /**
2797
     * @param mMailServer the mMailServer to set
2798
     */
2799
    public void setMailServer(String mMailServer) {
2800
        this.mMailServer = mMailServer;
2801
    }
2802
 
2803
    /**
2804
     * @return the mMailSender
2805
     */
2806
    public String getMailSender() {
2807
        return mMailSender;
2808
    }
2809
 
2810
    /**
2811
     * @param mMailSender the mMailSender to set
2812
     */
2813
    public void setMailSender(String mMailSender) {
2814
        this.mMailSender = mMailSender;
2815
    }
2816
 
2817
    /**
2818
     * @return the mMailGlobalTarget
2819
     */
2820
    public String getMailGlobalTarget() {
2821
        return mMailGlobalTarget;
2822
    }
2823
 
2824
    /**
2825
     * @param mMailGlobalTarget the mMailGlobalTarget to set
2826
     */
2827
    public void setMailGlobalTarget(String mMailGlobalTarget) {
2828
        this.mMailGlobalTarget = mMailGlobalTarget;
2829
    }
2830
 
2831
}