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