Subversion Repositories DevTools

Rev

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

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