Subversion Repositories DevTools

Rev

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