Subversion Repositories DevTools

Rev

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

Rev Author Line No. Line
6914 dpurdie 1
package com.erggroup.buildtool.ripple;
2
 
3
import java.io.File;
4
import java.sql.SQLException;
5
import java.util.ArrayList;
7048 dpurdie 6
import java.util.Comparator;
6914 dpurdie 7
import java.util.HashMap;
8
import java.util.Iterator;
9
import java.util.LinkedHashSet;
10
import java.util.Vector;
11
 
7033 dpurdie 12
import org.slf4j.Logger;
13
import org.slf4j.LoggerFactory;
6914 dpurdie 14
 
15
import com.erggroup.buildtool.ripple.ReleaseManager.BuildReason;
16
import com.erggroup.buildtool.smtp.CreateUrls;
17
import com.erggroup.buildtool.smtp.Smtpsend;
18
import com.erggroup.buildtool.utilities.MutableInt;
19
import com.erggroup.buildtool.utilities.XmlBuilder;
20
import com.erggroup.buildtool.utilities.utilities;
21
 
22
public class Package
23
{
24
    /**
25
     * name of package, must not contain spaces
26
     * 
27
     * @attribute
28
     */
29
    String mName = new String();
30
 
31
    /**
32
     * package scope
33
     * 
34
     * @attribute
35
     */
36
    String mExtension = new String();
37
 
38
    /**
39
     * instance identifier
40
     * 
41
     * @attribute
42
     */
43
    public String mVersion = new String();
7048 dpurdie 44
 
45
    /**
46
     * If this package is a candidate for building, then this value will be calculated
47
     * and will  be used if the package is selected for building
48
     */
49
    public String mNextVersion = null;
6914 dpurdie 50
 
51
    /**
52
     * Version string as specified by the user. Used with a ripple type of 'F'
53
     */
54
    String mFixedVersion = new String();
55
 
56
    /**
57
     * unique identifier 
58
     *      daemon builds = mName + mExtension
59
     *      escrow builds = mName + mVersion + mExtension
60
     * 
61
     * @attribute
62
     */
63
    public String mAlias = new String();
64
 
65
    /**
66
     * Version Control System Tag
67
     * 
68
     * @attribute
69
     */
70
    String mVcsTag = new String();
71
 
72
    /**
73
     * build standards
74
     * 
75
     * @attribute
76
     */
77
    public Vector<BuildStandard> mBuildStandardCollection = new Vector<BuildStandard>();
78
 
79
    /**
80
     * GBE_MACHTYPE used to build generic packages for this baseline only has
81
     * meaning in the daemon build, not the escrow build accessed by
82
     * BuildStandard::getPlatform, getBuildStandard
83
     * 
84
     * @attribute
85
     */
86
    public static String mGenericMachtype = System.getenv("GBE_MACHTYPE");
87
 
88
    /**
89
     * build dependencies by package alias
90
     * 
91
     * @attribute
92
     */
93
    public Vector<String> mDependencyCollection = new Vector<String>();
94
 
95
    /**
96
     * primary package version key pv_id in database
97
     * 
98
     * @attribute
99
     */
100
    public int mId;
101
 
102
    /**
103
     * indication of the nature of change
104
     * 
105
     * @attribute
106
     */
107
    Package.VersionNumberingStandard mChangeType = new VersionNumberingStandard();
108
 
109
    /**
110
     * determines what field is rippled on a package version whose dependencies
111
     * have changed
112
     * 
113
     * @attribute
114
     */
115
    Package.VersionNumberingStandard mRippleField = new VersionNumberingStandard();
116
 
117
    /**
118
     * interested owners
119
     * 
120
     * @attribute
121
     */
122
    private Vector<String> mBuildFailureEmailCollection = new Vector<String>();
123
 
124
    /**
125
     * when true will trigger unit tests as part of the package build phase in
126
     * daemon mode
127
     * 
128
     * @attribute
129
     */
130
    public boolean mHasAutomatedUnitTests = false;
131
 
132
    /**
133
     * when true, do not ripple this package through packages which are
134
     * dependent upon it in daemon mode
135
     * 
136
     * @attribute
137
     */
138
    public boolean mAdvisoryRipple = false;
139
 
140
    /**
141
     * Pegged packages will:
142
     *   - Not be rippled
143
     *   - Not be test buildable
144
     *   - Dependencies will not be considered
145
     *   Daemon Mode:
146
     *      Are not built in this release
147
     *      Are imported with out consideration as to their dependencies
148
     *      Must exist in dpkg_archive - will not be built if not present
149
     *   Escrow Mode:
150
     *      Have no special consideration
151
     *      The package and its dependencies will be built
152
     *
153
     * Packages imported into the release from an SDK will be treated as if they were
154
     * pegged by the built system.
155
     * 
156
     */
157
    public boolean mIsPegged = false;
158
    public boolean mIsSdk = false;
159
 
160
    /**
161
     * Unbuildable packages will not be candidates for a ripple or a rebuild
162
     * If they do not exist in dpkg_archive, then then will cause an error
163
     */
164
    public boolean mIsBuildable = true;
165
 
166
    /**
167
     * determines the build file the package is built in, or not
7048 dpurdie 168
     * <br>   1 Post Plan: build file - Result of planning
169
     * <br>   2 Post Plan: Future build requirement
170
     * <br>   3 Post Plan: Package has no build requirement
171
     * <br>   0 not yet processed (initial value) 
172
     * <br>  -1 not reproducible
173
     * <br>  -2 escrow: not reproducible on the build platforms configured for this release
174
     * <br>  -3 daemon: do not ripple
175
     * <br>  -4 directly dependent on package versions not in the baseline
176
     * <br>  -5 indirectly dependent on package versions which are not reproducible due to detected fault
177
     * <br>  -6 circular dependency
178
     * <br>  -7 Pegged or SDK Imported package not in dpkg_archive
179
     * <br>  -8 Pegged or SDK Imported package
180
     * <br>  -9 Rejected Daemon Instruction
181
     * <br>  -10 UnBuildable Package not in dpkg_archive
182
     * <br>  -11 Marked as RippleStop
6914 dpurdie 183
     * 
184
     */
185
    int mBuildFile = 0;
186
 
187
    /** Retained reason the package will not be built
188
     *  Save negative values as mBuildFile
189
     * @attribute
190
     */
191
    int mNoBuildReason = 0;
192
 
193
    /**
194
     * build dependencies by package
195
     * Calculated from information in mDependencyCollection
196
     * 
197
     * @attribute
198
     */
199
    Vector<Package> mPackageDependencyCollection = new Vector<Package>();
200
 
201
    /**
202
     * used for escrow build purposes set true when a package has been processed
203
     * 
204
     * @attribute
205
     */
206
    boolean mProcessed = false;
207
 
208
    /**
209
     * Reason that a package is being built
210
     */
211
    public BuildReason mBuildReason = null;
212
 
213
    /**
214
     * set true for WIP package versions only used in daemon mode
215
     * 
216
     * @attribute
217
     */
218
    public boolean mDirectlyPlanned = false;
219
 
220
    /**
221
     * set true when it is determined to be ripple built
222
     * 
223
     * @attribute
224
     */
225
    boolean mIndirectlyPlanned = false;
226
 
227
    /**
228
     * non zero instruction number when it is determined to be ripple built by force
229
     * 
230
     * @attribute
231
     */
7048 dpurdie 232
    public int mForcedRippleInstruction = 0;
6914 dpurdie 233
 
234
    /**
235
     * test build - non zero instruction number when it is determined to be test built
236
     * 
237
     * @attribute
238
     */
7048 dpurdie 239
    public int mTestBuildInstruction = 0;
6914 dpurdie 240
 
241
    /**
242
     * build dependencies by pv_id (-1 or not used for planned dependencies)
243
     * 
244
     * @attribute
245
     */
246
    public Vector<Integer> mDependencyIDCollection = new Vector<Integer>();
247
 
248
    /**
249
     * unique pkg_id in the database used for querying package version existence
250
     * in the database in daemon mode
251
     * 
252
     * @attribute
253
     */
254
    public int mPid;
255
 
256
    /**
257
     * maximum major number supported for determining ripple number
258
     * 
259
     * @attribute
260
     */
261
    int mMajorLimit;
262
 
263
    /**
264
     * maximum minor number supported for determining ripple number
265
     * 
266
     * @attribute
267
     */
268
    int mMinorLimit;
269
 
270
    /**
271
     * maximum patch number supported for determining ripple number
272
     * 
273
     * @attribute
274
     */
275
    int mPatchLimit;
276
 
277
    /**
278
     * maximum build number number supported for determining ripple number
279
     * 
280
     * @attribute
281
     */
282
    int mBuildLimit;
283
 
284
    /**
285
     * Logger
286
     * 
287
     * @attribute
288
     */
7033 dpurdie 289
    private static final Logger mLogger = LoggerFactory.getLogger(Package.class);
6914 dpurdie 290
 
291
    /**
292
     * dpkg archive location - The writable dpkg_archive
293
     * 
294
     * @attribute
295
     */
296
    public static String mGbeDpkg = System.getenv("GBE_DPKG");
297
 
298
    /**
299
     *  A physically close replica of dpkg_archive
300
     *  Is not writable 
301
     */
302
    public static final String mGbeDpkgReplica = System.getenv("GBE_DPKG_REPLICA");
303
 
304
    /**
305
     * Exception message used upon detection an archive does not exist Seems
306
     * this is a rare but transient and recoverable scenario
307
     * 
308
     * @attribute
309
     */
310
    public static final String mRecoverable = "dpkg_archive does not exist, recovery will be attempted";
311
 
312
    /**
313
     * true if the package exists in the package archive (dpkg_archive)
314
     * 
315
     * @attribute
316
     */
317
    private boolean mArchivalExistence = true;
318
 
319
    /**
320
     * when true will trigger source control interaction eg labelling
321
     * 
322
     * @attribute
323
     */
324
    public boolean mRequiresSourceControlInteraction = true;
325
 
326
    /**
327
     * when true has been checked for circular dependency
328
     * 
329
     * @attribute
330
     */
331
    boolean mCheckedCircularDependency = false;
332
 
333
    /**
334
     * when true has circular dependency, or in the process of detecting a circular dependency
335
     * 
336
     * @attribute
337
     */
338
    boolean mHasCircularDependency = false;
339
 
340
    /**
341
     * Bread crumb used to detect circular dependencies
342
     * 0 - Normal state
343
     * 1 - Crumb has been set
344
     * 2 - Crumb is a part of a circular dependency
345
     * 3 - Crumb is the start/end of the loop
346
     */
347
    int mBreadCrumb = 0;
348
 
349
    /**
350
     * Planned packages (WIPS) need calculate a new version number
351
     * This should be based on the Version of the package that the WIP was based upon
352
     */
353
	public String mPrevVersion;
354
 
7048 dpurdie 355
    /** Used to preserve the basic ordering of the package list(s)
356
     *  Only valid for the duration of the comparison sorts
357
     */
358
    int mSeqId;	
359
 
6914 dpurdie 360
	/** Indicates that the package not be ripple built at this time
361
	 *  Sequence is:
362
	 *     User sets to 's'
363
	 *     This buildtool will set to 'w' when a ripple has been detected
364
	 *     User resets to NULL when the ripple can be resumed. This is stored as an 'n'
365
	 * 
366
	 */
367
    public char mRippleStop;
7048 dpurdie 368
 
369
    /** The duration (seconds) of the last build of this package
370
     *  Used to estimate the cost of building the package
371
     *  A WIP packages gets the buildTime of the package its based upon
372
     */
373
    public int mBuildTime;
6914 dpurdie 374
 
7070 dpurdie 375
    /** Used in the calculation of a ripple plan
376
     * 
377
     */
378
    public int mRippleOrder = 0;
379
 
7048 dpurdie 380
    /** Set to indicate that the package is NOT a part of the full release set
381
     *  The package is a WIP, TestBuild or a ForcedRipple
382
     *  
383
     *   Used to simplify detection of such packages and to limit processing of these packages
384
     *   May be cleared if the package is added to the full set
385
     */
386
    public boolean mIsNotReleased = false;
7070 dpurdie 387
 
388
    /** Used by some algorithms for various indications
389
     * 
390
     */
391
    public boolean mIsProcessed = false;
7048 dpurdie 392
 
6914 dpurdie 393
    /**
394
     * Constructor
395
     * Used to create planned packages. In these packages the change_type is significant
396
     * 
397
     * @param pkg_id	   Package Name Identifier
398
     * @param pv_id        Package Version Id
399
     * @param pkg_name     Package Name
400
     * @param pkg_version  Package Version
401
     * @param v_ext        Package Suffix
402
     * @param alias        Package Alias
403
     * @param pkg_vcs_tag  Vcs Tag
404
     * @param ripple_field Ripple Field
405
     * @param change_type  Change Type
406
     */
7051 dpurdie 407
    public Package(int pkg_id, int pv_id, String pkg_name, String pkg_version, String v_ext, String alias, String pkg_vcs_tag, char ripple_field, char change_type)
6914 dpurdie 408
    {
409
        mLogger.debug("Package 1: pv_id " + pv_id + " pkg_name " + pkg_name + " v_ext " + v_ext + " alias " + alias
410
                + " pkg_vcs_tag " + pkg_vcs_tag + " change_type " + change_type);
411
        mId = pv_id;
412
        mName = pkg_name;
413
        mPid = pkg_id;
414
        mVersion = "0.0.0000";
415
        mExtension = v_ext;
416
        mAlias = alias;
417
        mVcsTag = pkg_vcs_tag;
418
 
419
        // Remove the package suffix from package_version to create the fixed version number
420
        mFixedVersion = pkg_version;
421
        mFixedVersion = mFixedVersion.substring(0, mFixedVersion.length() - mExtension.length());
422
 
423
        // a ripple_field of 'L' indicates this package has limited version numbering
424
        if (change_type == 'M') {
425
            mChangeType.setMajor(ripple_field == 'L' ? true : false);
426
 
427
        } else if (change_type == 'N') {
428
            mChangeType.setMinor(ripple_field == 'L' ? true : false);
429
 
430
        } else if (change_type == 'P') {
431
            mChangeType.setPatch(ripple_field == 'L' ? true : false);
432
 
433
        } else if (change_type == 'F') {
434
            mChangeType.setFixed();
435
 
436
        } else {
437
            mChangeType.setUnknown();
438
        }
439
    }
440
 
441
    /**
442
     * Constructor
443
     * Used to create existing packages, in these packages the ripple_field is significant
444
     * 
445
     * @param pkg_id	   Package Name Identifier
446
     * @param pv_id        Package Version Id
447
     * @param pkg_name     Package Name
448
     * @param pkg_version  Package Version
449
     * @param v_ext        Package Suffix
450
     * @param alias        Package Alias
451
     * @param pkg_vcs_tag  Vcs Tag
452
     * @param ripple_field Ripple Field
453
     */
7051 dpurdie 454
    public Package(int pkg_id,int pv_id, String pkg_name, String pkg_version, String v_ext, String alias, String pkg_vcs_tag, char ripple_field)
6914 dpurdie 455
    {
456
        mLogger.debug("Package 2: pv_id " + pv_id + " pkg_name " + pkg_name + " pkg_version " + pkg_version + " v_ext "
457
                + v_ext + " alias " + alias + " pkg_vcs_tag " + pkg_vcs_tag + " ripple_field " + ripple_field);
458
        mId = pv_id;
459
        mName = pkg_name;
460
        mPid = pkg_id;
461
        mVersion = pkg_version;
462
        int endindex = mVersion.length() - v_ext.length();
463
 
464
        if (endindex > 0)
465
        {
466
            mVersion = mVersion.substring(0, endindex);
467
        }
468
 
469
        mExtension = v_ext;
470
        mAlias = alias;
471
        mVcsTag = pkg_vcs_tag;
472
 
473
        // setBuild is the default
474
        if (ripple_field == 'M') {
475
            mRippleField.setMajor();
476
 
477
        } else if (ripple_field == 'm') {
478
            mRippleField.setMinor();
479
 
480
        } else if (ripple_field == 'p') {
481
            mRippleField.setPatch();
482
 
483
        } else if (ripple_field == 'L') {
484
            mRippleField.setLimit();
485
        }
486
    }
487
 
488
    /**
489
     * constructor
490
     */
491
    Package()
492
    {
493
        mLogger.debug("Package 3");
494
        mId = 0;
495
        mName = "null";
496
        mExtension = "null";
497
        mAlias = "null";
498
        mVcsTag = "null";
499
    }
500
 
501
    /**
7049 dpurdie 502
     * Constructor for unit test purposes
7051 dpurdie 503
     *  Will invoke applyPV and save the results for the UTF framework
6914 dpurdie 504
     */
505
    public Package(ReleaseManager rm, String version, int majorLimit, int minorLimit, int patchLimit, int buildNumberLimit)
506
    {
507
        mId = -1;
508
        mRippleField.setLimit();
509
        mVersion = version;
510
        mMajorLimit = majorLimit;
511
        mMinorLimit = minorLimit;
512
        mPatchLimit = patchLimit;
513
        mBuildLimit = buildNumberLimit;
514
 
515
        if (version.endsWith(".cots"))
516
        {
517
            mExtension = ".cots";
518
            mVersion = version.substring(0, version.length() - 5);
519
            mChangeType.setMajor(false);
520
            mChangeType.setMinor(false);
521
            mChangeType.setPatch(true);
522
            mRippleField.setBuild();
523
        }
524
 
525
        try
526
        {
7050 dpurdie 527
            mId = applyPV(rm);
6914 dpurdie 528
        } catch (Exception e)
529
        {
530
        }
531
    }
532
 
533
    /**
7051 dpurdie 534
     * Constructor for unit test purposes
535
     * Performs a partial copy of a package - sufficient for test purposes
536
     * @param base      - Base package  
537
     * @param newPvId   - New pvid of the package
538
     */
7078 dpurdie 539
 
7051 dpurdie 540
    public Package(int newPvId, Package base) {
541
 
542
        mId = newPvId;
543
        mPid = base.mPid;
544
        mName = base.mName;
545
        mExtension = base.mExtension;
546
        mVersion = base.mVersion;
547
        mAlias = base.mAlias;
548
        mVcsTag = base.mVcsTag;
549
        mFixedVersion = base.mFixedVersion;
550
        mChangeType = base.mChangeType;
551
        mRippleField = base.mRippleField;
552
        mBuildTime = base.mBuildTime;
553
        mHasAutomatedUnitTests = base.mHasAutomatedUnitTests;
554
        mSeqId = base.mSeqId;
555
 
7078 dpurdie 556
        mBuildFailureEmailCollection = new Vector<String>(base.mBuildFailureEmailCollection);
557
        mPackageDependencyCollection = new Vector<Package>(base.mPackageDependencyCollection);
558
        mDependencyIDCollection = new Vector<Integer>(base.mDependencyIDCollection);
559
        mDependencyCollection = new Vector<String>(base.mDependencyCollection);
560
        mBuildStandardCollection = new Vector<BuildStandard>( base.mBuildStandardCollection);
7051 dpurdie 561
 
562
    }
7078 dpurdie 563
 
564
    /** Generate a nice text string useful for debugging
565
     * 
566
     */
567
    public String toString()
568
    {
569
        return mId + ":" + mAlias;
570
    }
7051 dpurdie 571
 
572
    /**
6914 dpurdie 573
     * accessor for unit test purposes
574
     */
575
    public int getId()
576
    {
577
        return mId;
578
    }
579
 
580
    /**
581
     * accessor for unit test purposes
582
     */
583
    public String getVersion()
584
    {
585
        return mVersion;
586
    }
587
 
7049 dpurdie 588
    /**
589
     * accessor for unit test purposes
590
     */
591
    public String getNextVersion()
592
    {
593
        return mNextVersion;
594
    }
595
 
6914 dpurdie 596
 
597
    /**
598
     * returns true if mBuildStandardCollection is not empty
599
     */
600
    boolean isReproducible()
601
    {
7048 dpurdie 602
        mLogger.debug("isReproducible on Package {}", mName);
6914 dpurdie 603
        boolean retVal = ! mBuildStandardCollection.isEmpty();
7048 dpurdie 604
        mLogger.info("isReproducible returned {}", retVal);
6914 dpurdie 605
        return retVal;
606
    }
607
 
608
 
609
    /**
610
     * returns true if at least one of its BuildStandards has mGeneric true
611
     */
612
    boolean isGeneric()
613
    {
7048 dpurdie 614
        mLogger.debug("isGeneric on Package {}", mName);
6914 dpurdie 615
        boolean retVal = false;
616
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
617
        {
618
            BuildStandard buildStandard = it.next();
619
 
620
            if (buildStandard.isGeneric())
621
            {
622
                retVal = true;
623
                break;
624
            }
625
        }
626
 
7048 dpurdie 627
        mLogger.info("isGeneric returned {}", retVal);
6914 dpurdie 628
        return retVal;
629
    }
630
 
631
    /**
632
     * Returns true if at least one of the packages build standards can be 
633
     * built in the named machine class. 
634
     *  
635
     * Used to determine if the package can be built with the current buildset 
636
     * by iteration over all machine classes in the buildset. 
637
     */
638
    boolean canBeBuildby(String machineClass)
639
    {
640
        mLogger.debug("canBeBuildby on Package " + mName);
641
        boolean retVal = false;
642
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
643
        {
644
            BuildStandard buildStandard = it.next();
645
 
646
            if (buildStandard.isGeneric())
647
            {
648
                retVal = true;
649
                break;
650
            }
651
 
652
            if (buildStandard.mMachClass.equals(machineClass))
653
            {
654
                retVal = true;
655
                break;
656
            }
657
        }
658
 
659
        mLogger.info("canBeBuildby returned " + retVal);
660
        return retVal;
661
    }
662
 
663
    /**
664
     * Compare the build standards of two packages 
665
     * Used only in escrow mode 
666
     * Used to compare a package and its dependents (one by one) 
667
     *  
668
     * Returns true if the parent (this) package  can be built in the same escrow 
669
     * build iteration as the dependent package. 
670
     *  
671
     * This is a complex decision and has a few built in assumptions. 
672
     *  
673
     * If the dependent package is 'generic' then the parent package can be built 
674
     *  
675
     * If the parent package is generic then is can only be built if the dependent is 
676
     * also generic. Otherwise we must assume that the parent package is an agregator 
677
     * package and requires artifacts from the dependent package built by all of its 
678
     * required build machines. 
679
     *  
680
     * If both packages are not generic, then if the build standards of the parent and the 
681
     * dependent are identical then 
682
     *  
683
     *      If we have one build standard, we can build the parent in this iteration as
684
     *      the dependent package has been completely built.
685
     *  
686
     *      If we have more than one build standard ( but they are identical ) then we
687
     *      ASSUME that we can build the parent in this iteration. The assumption is that
688
     *      there is no mixing between build machines. ie: Windows consumer users Windows
689
     *      artifacts and the Linux consumer uses Linux artifacts ...
690
     *  
691
     * If both packages are not generic and the build standards are not identical, then 
692
     * things get hard. The safest solution is to assume the parent cannot be built in this 
693
     * iteration. This is not a bad assumption. 
694
     *  
695
     */
696
    boolean haveSameBuildStandards(Package d)
697
    {
698
        HashMap<String, Boolean> standardSet = new HashMap<String, Boolean>();
699
        boolean isGeneric = false;
700
        boolean isGeneric_d = false;
701
        boolean isIdentical = true;
702
 
703
        // Scan the build standards of the parent package and create a hash map for each machine
704
        // class required by the parent. Also determine if the parent is generic.
705
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
706
        {
707
            BuildStandard bs = it.next();
708
            standardSet.put(bs.mMachClass, false);
709
 
710
            if (bs.isGeneric())
711
            {
712
                isGeneric = true;
713
                break;
714
            }
715
        }
716
 
717
        // Scan the build standards in the dependent package and remove items from the map
718
        // If it was not in the map then the dependent package builds for platforms that the
719
        // parent package does not - thus the build standards cannot be identical
720
        // Also determine if the dependent is generic.
721
        for (Iterator<BuildStandard> it = d.mBuildStandardCollection.iterator(); it.hasNext();)
722
        {
723
            BuildStandard bs = it.next();
724
             if (bs.isGeneric())
725
             {
726
             isGeneric_d = true;
727
             break;
728
             }
729
 
730
            Boolean value = standardSet.remove(bs.mMachClass);
731
            if (value == null)
732
            {
733
                isIdentical = false;
734
                break;
735
            }
736
        }
737
 
738
        //
739
        // If there are any items left in the map then the parent package builds on machines
740
        // that the dependent does not. The two are not identical.
741
 
742
        if (!standardSet.isEmpty())
743
        {
744
            isIdentical = false;
745
        }
746
 
747
        // If dependent is generic, then it will be build on the first platform in this iteration
748
        // All is good
749
 
750
        if( isGeneric_d)
751
            return true;
752
 
753
        //  If I am generic and the dependent is generic, then I can be built at this time
754
        // 
755
        //  Will not reach here as we have already said that if dependenbt is generic, then all is good
756
        //  if (isGeneric_d && isGeneric)
757
        //  {
758
        //      return true;
759
        //  }
760
 
761
 
762
        //  If I am generic then I must wait for ALL dependent to be built on all platforms
763
        //  Thus I can't be built this round
764
        //
765
        if (isGeneric)
766
            return false;
767
 
768
        //
769
        //  If the two sets of build standards don't have generic, BUT are identical
770
        //  the we can assume that there is no cross breading. and that windows bits build from windows bits
771
        //  and solaris bits build from solaris
772
        //
773
        if (isIdentical)
774
            return true;
775
 
776
        //  The two sets of build standards are not an exact match
777
        //  Cheap solution: Assume that I can't be built this round
778
        //  Possible solution: If I am a subset of the dependent - then I can be built
779
        //                     If I am a superset of the dependent - then ???
780
        return false;
781
 
782
    }
783
 
784
    /**
7048 dpurdie 785
     * Applies the required version number change. Will calculate mNextVersion
7050 dpurdie 786
     * while not changing mVersion.
6914 dpurdie 787
     * 
788
     * @param releaseManager    Release Manager instance to work against
789
     * 
7048 dpurdie 790
     * @return 0 : success
791
     *         1 : cannot work with non standard versioning
792
     *         2 : ripple field limitations prevent a ripple build
793
     *         3 : Invalid Change Type
6914 dpurdie 794
     * @exception Exception
795
     */
7050 dpurdie 796
 
797
    int applyPV(ReleaseManager releaseManager) throws Exception
6914 dpurdie 798
    {
799
        String logInfo = "applyPV," + mName;
800
        mLogger.debug("applyPV on Package " + mName);
7048 dpurdie 801
 
6914 dpurdie 802
        //
7048 dpurdie 803
        //  This method used to actually perform the version change
804
        //  Now it just calculates the potential value
805
        //  Must not alter mVersion as it will be used if the package is not selected to be built,
806
        //  but some of the code assumes that it can be.
807
        //
808
        String originalVersion = mVersion;
809
 
810
        //
6914 dpurdie 811
        // Four scenarios, only applyPV for 3 of them
812
        // mDirectlyPlanned mIndirectlyPlanned mArchivalExistence mForcedRipple
813
        // Action
814
        // WIP/test build exists: true true don't care don't care applyPV
815
        // Package version is out of date: false true true don't care applyPV
816
        // Forced ripple: false true don't care > 0 applyPV
817
        // Package version does not exist: false true false = 0 do not applyPV
818
        //
819
        if (!mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence && mForcedRippleInstruction == 0)
820
        {
821
            // the package has an mIndirectlyPlanned flag set true in daemon
822
            // mode because the package does not exist in an archive
823
            // do not apply a different package version
7048 dpurdie 824
            mLogger.info("applyPV. Rebuild Package {}", mName);
6914 dpurdie 825
            mLogger.info("applyPv returned 0");
826
            return 0;
827
        }
828
 
829
        // override - no longer doing a rebuild - version number change from this point on
830
        if (mTestBuildInstruction == 0)
831
        {
832
            mRequiresSourceControlInteraction = true;
833
        }
834
 
835
        //	Force test builds to use a sensible version number
836
        if (mTestBuildInstruction > 0 )
837
        {
838
        	mChangeType.resetData();		// Resolve conflict via build numbers
839
        	mRippleField.setBuild();
840
        	mVersion = "99.99.98999";		// Such that rippling build number will goto 99.99.99000
841
        }
842
 
843
        //
844
        // Detect invalid change type
845
        // Flagged when package instance is created
846
        //
847
        if (mChangeType.mUnknown)
848
        {
7048 dpurdie 849
            mLogger.info("Package Version specified on Package {} New Version: {}", mName, mVersion);
6914 dpurdie 850
            mLogger.info("applyPv returned 3");
851
            return 3;
852
        }
853
 
854
        // If we are not calculating the new package version because the user
855
        // has fixed the version of the package. We are given the new package version.
856
        if (mChangeType.mFixed)
857
        {
858
            // mVersion is already setup
859
 
7048 dpurdie 860
            mNextVersion = mFixedVersion;
861
            mLogger.info("Package Version specified on Package {} New Version: {}", mName,  mVersion);
6914 dpurdie 862
            mLogger.info("applyPv returned 0");
863
            return 0;
864
        }
865
 
866
        // We need to calculate the new version number
867
        //
868
        MutableInt major = new MutableInt(0);
869
        MutableInt minor = new MutableInt(0);
870
        MutableInt patch = new MutableInt(1000);
871
 
872
        // Planned packages have a previous version number to be used as the bases for the
873
        // calculation. Ripples won't.
874
        //
875
        if (mPrevVersion != null)
876
        {
877
        	mVersion = mPrevVersion;
878
        }
879
        String[] field = mVersion.split("\\D");
880
        String nonStandardCotsVersion = "";
881
        logInfo += ", Prev:" + mVersion;
882
 
883
        if (field.length == 3)
884
        {
885
            major.value = Integer.parseInt(field[0]);
886
            minor.value = Integer.parseInt(field[1]);
887
            patch.value = Integer.parseInt(field[2]);
888
        } 
889
        else
890
        {
891
            //
892
            // Can ripple a .cots package under very controlled conditions
893
            // Its ends with a .patchBuild field
894
            // Package is marked as ripple via build number
895
            // Change type of Major and Minor are not allowed
896
            //
7048 dpurdie 897
            if (!mChangeType.mMajor && !mChangeType.mMinor && mRippleField.mBuild && mExtension.compareTo(".cots") == 0 && field.length > 0)
6914 dpurdie 898
            {
899
                // allow and work with (ripple build) versions a.b.c.d....xxxx
900
                // where xxxx.length > 3
901
                String patchStr = field[field.length - 1];
902
                int patchLen = patchStr.length();
903
 
904
                // check patchStr is the last (at least 4) digits
905
                if (patchLen > 3
906
                        && mVersion.substring(mVersion.length() - patchLen, mVersion.length()).compareTo(patchStr) == 0)
907
                {
908
                    patch.value = Integer.parseInt(patchStr);
909
                    nonStandardCotsVersion = mVersion.substring(0, mVersion.length() - patchLen);
910
                }
911
            }
912
 
913
            if (nonStandardCotsVersion.length() == 0)
914
            {
915
                // cannot work with non standard versioning
916
                mLogger.error("applyPV cannot work with non standard versioning");
917
                mLogger.info("applyPv returned 1");
918
                return 1;
919
            }
920
        }
921
 
922
        if (nonStandardCotsVersion.length() == 0 && patch.value < 1000 && field[2].substring(0, 1).compareTo("0") != 0)
923
        {
7048 dpurdie 924
            mLogger.info("applyPV accomodate old style Version of the form 1.0.1");
6914 dpurdie 925
            patch.value = patch.value * 1000;
926
        }
927
 
928
        // mChangeType overrides mRippleField
929
        do
930
        {
931
            if (mChangeType.mMajor)
932
            {
933
                logInfo += ",CT Major";
934
                if (!incrementFieldsAccordingToLimits(4, major, minor, patch))
935
                {
936
                    mLogger.info("applyPv returned 2");
7048 dpurdie 937
                    mVersion = originalVersion;
6914 dpurdie 938
                    return 2;
939
                }
940
            } else if (mChangeType.mMinor)
941
            {
942
                logInfo += ",CT Minor";
943
                if (!incrementFieldsAccordingToLimits(3, major, minor, patch))
944
                {
945
                    mLogger.info("applyPv returned 2");
7048 dpurdie 946
                    mVersion = originalVersion;
6914 dpurdie 947
                    return 2;
948
                }
949
            } else if (mChangeType.mPatch)
950
            {
951
                logInfo += ",CT Patch";
952
                if (!incrementFieldsAccordingToLimits(2, major, minor, patch))
953
                {
954
                    mLogger.info("applyPv returned 2");
7048 dpurdie 955
                    mVersion = originalVersion;
6914 dpurdie 956
                    return 2;
957
                }
958
            } else
959
            {
960
                if (mRippleField.mMajor)
961
                {
962
                    logInfo += ",R Major";
963
                    major.value++;
7048 dpurdie 964
                    mLogger.info("applyPV mRippleField.mMajor {}", major.value);
6914 dpurdie 965
                    minor.value = 0;
966
                    patch.value = 0;
967
                } else if (mRippleField.mMinor)
968
                {
969
                    logInfo += ",R Minor";
970
                    minor.value++;
7048 dpurdie 971
                    mLogger.info("applyPV mRippleField.mMinor {}", minor.value);
6914 dpurdie 972
                    patch.value = 0;
973
                } else if (mRippleField.mPatch)
974
                {
975
                    logInfo += ",R Patch";
976
                    patch.value = ((patch.value / 1000) + 1) * 1000; 
7048 dpurdie 977
                    mLogger.info("applyPV mRippleField.mPatch {}", patch.value);
6914 dpurdie 978
                } else if (mRippleField.mBuild)
979
                {
980
                    logInfo += ", R Build";
981
                    patch.value++;
7048 dpurdie 982
                    mLogger.info("applyPV mRippleField.mBuild {}", patch.value);
6914 dpurdie 983
                } else
984
                {
985
                    if (!incrementFieldsAccordingToLimits(1, major, minor, patch))
986
                    {
987
                        mLogger.info("applyPv returned 2");
7048 dpurdie 988
                        mVersion = originalVersion;
6914 dpurdie 989
                        return 2;
990
                    }
991
                }
992
            }
993
 
994
            if (nonStandardCotsVersion.length() == 0)
995
            {
996
                mVersion = String.valueOf(major.value) + "." + String.valueOf(minor.value) + ".";
997
            } else
998
            {
999
                mVersion = nonStandardCotsVersion;
1000
            }
1001
 
1002
            if (patch.value < 10)
1003
            {
1004
                mVersion += "000";
1005
            } else if (patch.value < 100)
1006
            {
1007
                mVersion += "00";
1008
            } else if (patch.value < 1000)
1009
            {
1010
                mVersion += "0";
1011
            }
1012
 
1013
            mVersion += String.valueOf(patch.value);
7050 dpurdie 1014
        } while (exists(releaseManager));
6914 dpurdie 1015
 
7048 dpurdie 1016
        logInfo += ", Next Version:" + mVersion;
7033 dpurdie 1017
        mLogger.error(logInfo);
6914 dpurdie 1018
        mLogger.info("applyPv returned 0");
7048 dpurdie 1019
        mNextVersion = mVersion;
1020
        mVersion = originalVersion;
6914 dpurdie 1021
        return 0;
1022
    }
1023
 
1024
    /**
1025
     * increments fields according to mRippleField.mLimit if necessary will
1026
     * apply it to the field passed as follows 1 = build 2 = patch 3 = minor
1027
     * other = major returns true on success false on ripple field limitations
1028
     * prevent a ripple build
1029
     */
1030
    private boolean incrementFieldsAccordingToLimits(int field, MutableInt major, MutableInt minor, MutableInt patch)
1031
    {
1032
        boolean retVal = true;
1033
 
1034
        if (!mChangeType.mLimit && !mRippleField.mLimit)
1035
        {
1036
            // simple case
1037
            // no need to take field limits into consideration
1038
            switch (field)
1039
            {
1040
            case 1:
1041
                // unreachable
1042
                // the only scenario involving build number manipulation
1043
                // involves the mRippleField.mLimit being set
1044
                retVal = false;
1045
                break;
1046
            case 2:
1047
                do
1048
                {
1049
                    patch.value++;
1050
                } while ((patch.value / 1000) * 1000 != patch.value);
1051
                mLogger.info("incrementFieldsAccordingToLimits patch " + patch.value);
1052
                break;
1053
            case 3:
1054
                minor.value++;
1055
                mLogger.info("incrementFieldsAccordingToLimits minor " + minor.value);
1056
                patch.value = 0;
1057
                break;
1058
            default:
1059
                major.value++;
1060
                mLogger.info("incrementFieldsAccordingToLimits major " + major.value);
1061
                minor.value = 0;
1062
                patch.value = 0;
1063
            }
1064
        } else
1065
        {
1066
            // take field limits into consideration
1067
            boolean changeOccurred = false;
1068
            boolean incrementField = true;
1069
 
1070
            switch (field)
1071
            {
1072
            case 1:
1073
                if (mBuildLimit != 0)
1074
                {
1075
                    // increment or reset the patch build number
1076
                    int buildNumber = patch.value - (patch.value / 1000) * 1000;
1077
 
1078
                    if (buildNumber < mBuildLimit)
1079
                    {
1080
                        // can increment the patch build number
1081
                        patch.value++;
1082
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit build number " + patch.value);
1083
                        changeOccurred = true;
1084
                        incrementField = false;
1085
                    } else
1086
                    {
1087
                        if (mPatchLimit == 0)
1088
                        {
1089
                            // reset the patch number and patch build number
1090
                            patch.value = 0;
1091
                        }
1092
                    }
1093
                }
1094
                // no break by design
1095
            case 2:
1096
                if (mPatchLimit != 0 && incrementField)
1097
                {
1098
                    // increment or reset the patch number
1099
                    if ((patch.value / 1000) < mPatchLimit)
1100
                    {
1101
                        do
1102
                        {
1103
                            patch.value++;
1104
                        } while ((patch.value / 1000) * 1000 != patch.value);
1105
 
1106
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit patch " + patch.value);
1107
                        changeOccurred = true;
1108
                        incrementField = false;
1109
                    } else
1110
                    {
1111
                        // reset the patch number and patch build number
1112
                        patch.value = 0;
1113
                    }
1114
                }
1115
                // no break by design
1116
            case 3:
1117
                if (mMinorLimit != 0 && incrementField)
1118
                {
1119
                    // increment or reset the minor number
1120
                    if (minor.value < mMinorLimit)
1121
                    {
1122
                        minor.value++;
1123
                        patch.value = 0;
1124
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit minor " + minor.value);
1125
                        changeOccurred = true;
1126
                        incrementField = false;
1127
                    } else
1128
                    {
1129
                        // reset the minor number
1130
                        minor.value = 0;
1131
                    }
1132
                }
1133
                // no break by design
1134
            default:
1135
                if (mMajorLimit != 0 && incrementField)
1136
                {
1137
                    // increment or reset the major number
1138
                    if (major.value < mMajorLimit)
1139
                    {
1140
                        // increment the major number
1141
                        changeOccurred = true;
1142
                        major.value++;
1143
                        minor.value = 0;
1144
                        patch.value = 0;
1145
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit major " + major.value);
1146
                    }
1147
                }
1148
            }
1149
 
1150
            if (!changeOccurred)
1151
            {
1152
                // unable to increment a field due to field limitations
1153
                mLogger.error("incrementFieldsAccordingToLimits ripple field limitations prevent a ripple build");
1154
                mLogger.info("incrementFieldsAccordingToLimits returned false");
1155
                retVal = false;
1156
            }
1157
        }
1158
 
1159
        return retVal;
1160
    }
1161
 
1162
    /**
7048 dpurdie 1163
     * Check if a specified Version of the package exists in dpkg_archive or the Release Manager Database
6914 dpurdie 1164
     * 
1165
     * @param releaseManager Release Manager Instance
1166
     * 
1167
     * @return True if the Package Version exists within the Release Manager Database
1168
     * @exception Exception
1169
     */
7050 dpurdie 1170
    private boolean exists(ReleaseManager releaseManager) throws Exception
6914 dpurdie 1171
    {
1172
        mLogger.debug("exists on Package " + mName + " version " + mVersion + " extension " + mExtension);
1173
        boolean retVal = false;
1174
 
7048 dpurdie 1175
        if (!releaseManager.mUseDatabase)
6914 dpurdie 1176
        {
1177
            mLogger.info("exists !releaseManager.mUseDatabase");
1178
        }
1179
        else
1180
        {
1181
            //  Check Package Archive
1182
            retVal = existsInDpkgArchive();
1183
            if (!retVal)
1184
            {
1185
                //  Check Release Manager Database
1186
                retVal = releaseManager.queryPackageVersions(mPid, mVersion + mExtension);
1187
            }
1188
        }
1189
 
1190
        mLogger.info("exists returned " + retVal);
1191
        return retVal;
1192
    }
1193
 
1194
 
1195
    /**
1196
     * Check to see if a package exists in dpkg_archive
1197
     * 
1198
     * @return true if the version exists in dpkg_archive
1199
     * @exception Exception Thrown if dpkg_archive does not exist. The 'cause' of 'mRecoverable' is special and
1200
     *                      will be trapped later to determine if this is a recoverable exception.
1201
     */
1202
    boolean existsInDpkgArchive() throws Exception
1203
    {
1204
        mLogger.debug("existsInDpkgArchive on " + mName);
1205
        boolean retVal = false;
1206
        String name = utilities.catDir(mName, mVersion + mExtension );
1207
 
1208
        //  If a replica exists, then check it first
1209
        //  If we are configured with a replica its because access to the main archive is slow
1210
        //  and we want this check to be fast
1211
        //
1212
        if (mGbeDpkgReplica != null && mGbeDpkgReplica.length() > 0)
1213
        {
1214
            File dpkg = new File(mGbeDpkgReplica);
1215
            if (!dpkg.exists())
1216
            {
7033 dpurdie 1217
                mLogger.error("existsInDpkgArchive. mGbeDpkgReplica not accessable. " + mRecoverable);
6914 dpurdie 1218
                throw new Exception(mRecoverable);
1219
            }
1220
 
1221
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkgReplica, name) ) )
1222
            {
1223
                mLogger.info("existsInDpkgArchive mGbeDpkgReplica");
1224
                retVal = true;
1225
            }
1226
        }
1227
 
1228
        //  Check (possibly remote) dpkg_archive for files existence if it was not found locally
1229
        //
1230
        if ( !retVal )
1231
        {
1232
 
1233
            //  If the package archive does not exist at the moment, then we have a network issue
1234
            //  This is a recoverable error
1235
 
1236
            File dpkg = new File(mGbeDpkg);
1237
            if (!dpkg.exists())
1238
            {
7033 dpurdie 1239
                mLogger.error("existsInDpkgArchive. mGbeDpkg not accessable. " + mRecoverable);
6914 dpurdie 1240
                throw new Exception(mRecoverable);
1241
            }
1242
 
1243
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkg, name) ) )
1244
            {
1245
                mLogger.info("existsInDpkgArchive mGbeDpkg");
1246
                retVal = true;
1247
            }
1248
        }
1249
 
1250
        mArchivalExistence = retVal;
1251
        mLogger.info("existsInDpkgArchive returned " + retVal);
1252
        return retVal;
1253
    }
1254
 
1255
    /**
1256
     * returns true if the required package archives (dpkg_archive) exist
1257
     * attempt to recover from their transient loss
1258
     */
1259
    public static boolean recover()
1260
    {
1261
        mLogger.debug("recover");
1262
        boolean retVal = false;
1263
 
1264
        String Release = mGbeDpkg;
1265
        if (Release != null)
1266
        {
1267
            if ( utilities.freshFileExists(mGbeDpkg) )
1268
            {
1269
                retVal = true;
7033 dpurdie 1270
                mLogger.error("recover: dpkg_archive access has been restored");
6914 dpurdie 1271
            }
1272
        }
1273
 
1274
        mLogger.info("recover returned " + retVal);
1275
        return retVal;
1276
    }
1277
 
1278
    /**
1279
     * Returns a data structure of unique email addresses
1280
     * Uses the Global Email Collection and the packages own failure email collection
1281
     */
1282
    private LinkedHashSet<String>buildEmailList(RippleEngine rippleEngine)
1283
    {
1284
        //  Create a single list of email targets ensuring only one instance of each email address
1285
        //  ie: Remove duplicates, null and empty strings
1286
        LinkedHashSet<String> hs = new LinkedHashSet<String>();
1287
 
1288
        // Global and Project Wide emails
1289
        for (Iterator<String> it = rippleEngine.mMailGlobalCollection.iterator(); it.hasNext();)
1290
        {
1291
            String item = it.next();
1292
            if (item != null && item.length() > 0) 
1293
            {
1294
                hs.add(item);
1295
            }
1296
        }
1297
 
1298
        // Package specific collection
1299
        for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext();)
1300
        {
1301
            String item = it.next();
1302
            if (item != null && item.length() > 0) 
1303
            {
1304
                hs.add(item);
1305
            }
1306
        }
1307
 
1308
        return hs;
1309
    }
1310
 
1311
    /**
1312
     * Add email information in a form suitable for creating an Ant file
1313
     * @param   rippleEngine    - Ripple Engine Instance
1314
     * @param   xml             - An XmlBuilder element to extend
1315
     */
1316
    void emailInfo(RippleEngine rippleEngine, XmlBuilder xml)
1317
    {
1318
 
1319
        //  Create a single list of email targets ensuring only one instance of each email address
1320
        //  ie: Remove duplicates
1321
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1322
 
1323
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1324
        {
1325
            String email = it.next();
1326
            XmlBuilder entry = xml.addNewElement("owner");
1327
            entry.addAttribute("email", email);
1328
        }
1329
    }
1330
 
1331
    /**
1332
     * Returns email information in a form suitable for direct use
1333
     * @param   rippleEngine Current Release Manager context
1334
     * @return  A comma separated list of user names. May return a 'null' String
1335
     */
1336
    String emailInfoNonAntTask(RippleEngine rippleEngine)
1337
    {
1338
        //  Create a single list of email targets ensuring only one instance of each email address
1339
        //  ie: Remove duplicates
1340
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1341
 
1342
        String retVal = null;
1343
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1344
        {
1345
            String email = it.next();
1346
 
1347
            if (retVal == null)
1348
            {
1349
                retVal = new String();
1350
            } else
1351
            {
1352
                retVal += ",";
1353
            }
1354
            retVal += email;
1355
        }
1356
 
1357
        return retVal;
1358
    }
1359
 
1360
    /**
1361
     * Adds email to mBuildFailureEmailCollection.
1362
     * Do not worry about multiple entries. These will be handled when the data is extracted
7048 dpurdie 1363
     * @param   email   - Email address. Null is allowed and will not be added
6914 dpurdie 1364
     */
1365
    public void addEmail(String email)
1366
    {
7048 dpurdie 1367
        if (email != null) 
6914 dpurdie 1368
        {
7048 dpurdie 1369
            mBuildFailureEmailCollection.add(email);
6914 dpurdie 1370
        }
1371
    }
1372
 
1373
    /**
1374
     * End of Processing for a Test Build
1375
     *     - sends email notification
1376
     *     - marks the instruction complete in the database
1377
     */
1378
    public void completeTestBuild(RippleEngine rippleEngine, boolean success) throws SQLException, Exception
1379
    {
1380
        mLogger.debug("completeTestBuild");
1381
 
1382
        if (mTestBuildInstruction == 0)
1383
        {
1384
            mLogger.info("completeTestBuild. Not Build Instruction");
1385
            return;
1386
        }
1387
 
1388
        //  Email Subject
1389
        String subject = (success == true ? "TEST BUILD COMPLETED SUCCESSFULLY" : "TEST BUILD FAILED") 
1390
                + " on package "
1391
                + mAlias;
1392
 
1393
        // Email Body
1394
        String mailBody = "";
1395
        if ( success != true)
1396
        {
1397
            mailBody += "Test build issues are identified in preceding build failure email.<p>"; 
1398
        }
1399
        mailBody += "Release: " + rippleEngine.mBaselineName + "<br>" 
1400
                  + "Package: " + mAlias + "<br>" 
1401
                  + "Rm Ref: " + CreateUrls.generateRmUrl(rippleEngine.getRtagId(), mId) + "<br>" 
1402
                  + "VcsTag: " + mVcsTag + "<br>"
1403
                  + "Build dependencies:<br>";
1404
 
1405
        String indentString = "&nbsp;&nbsp;&nbsp;&nbsp;";
1406
 
1407
        for (Iterator<Package> it3 = mPackageDependencyCollection.iterator(); it3.hasNext();)
1408
        {
1409
            Package depend = it3.next();
1410
 
1411
            String dependsExtension = depend.mExtension;
1412
            String dependsVersion = depend.mVersion;
1413
 
1414
            if (dependsExtension.length() > 0)
1415
            {
1416
                dependsVersion += dependsExtension;
1417
            }
1418
            mailBody += indentString + RippleEngine.quoteString(depend.mName, dependsVersion) + "<br>";
1419
        }
1420
 
1421
        mailBody += "<br>Build standards:<br>";
1422
 
1423
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
1424
        {
1425
            BuildStandard bs = it.next();
1426
 
1427
            String bsText = bs.getBuildStandardText();
1428
            if (bsText.length() > 0)
1429
            {
1430
                mailBody += indentString + bsText + "<br>";
1431
            }
1432
        }
1433
 
1434
        mailBody += "<p><hr>";
1435
        try
1436
        {
1437
            String target = emailInfoNonAntTask(rippleEngine);
7033 dpurdie 1438
            mLogger.error("completeTestBuildEmail Server: " + rippleEngine.getMailServer());
1439
            mLogger.error("completeTestBuildEmail Sender: " + rippleEngine.getMailSender());
1440
            mLogger.error("completeTestBuildEmail Target: " + target);
6914 dpurdie 1441
 
1442
            Smtpsend.send(rippleEngine.getMailServer(), // mailServer
1443
                    rippleEngine.getMailSender(),       // source
1444
                    target,                         // target
1445
                    rippleEngine.getMailSender(),       // cc
1446
                    null,                           // bcc
1447
                    subject,                        // subject
1448
                    mailBody,                       // body
1449
                    null                            // attachment
1450
            );
1451
        } catch (Exception e)
1452
        {
1453
            mLogger.warn("Email Failure: completeTestBuild:" + e.getMessage());
1454
        }
1455
 
1456
        // Update the Release Manager Database
1457
        rippleEngine.mReleaseManager.markDaemonInstCompleted(mTestBuildInstruction);
7033 dpurdie 1458
        mLogger.error("completeTest. Returning");
6914 dpurdie 1459
    }
1460
 
1461
 
1462
    /**
7078 dpurdie 1463
     * Returns true if the package is a part of a circular dependency within the provided collection
6914 dpurdie 1464
     * 
1465
     * If the package depends on a package with a circular dependency then the function
1466
     * will return false.
1467
     */
7078 dpurdie 1468
    public boolean hasCircularDependency(ArrayList<Package> packageCollection)
6914 dpurdie 1469
    {
7048 dpurdie 1470
        mLogger.debug("hasCircularDependency: {}", mAlias);
7078 dpurdie 1471
        boolean retVal = detectCircularDependency(mAlias, packageCollection, null);
7048 dpurdie 1472
        mLogger.info("hasCircularDependency returned {} ", retVal);
6914 dpurdie 1473
        return retVal;
1474
    }
7078 dpurdie 1475
 
1476
    /** Reset the circular dependency testing information
1477
     * 
1478
     * @param packageCollection - Collection of packages to process
1479
     */
1480
    public static void resetCircularDependency(ArrayList<Package> packageCollection)
1481
    {
1482
        for (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); )
1483
        {
1484
            Package p = it.next();
1485
            p.mCheckedCircularDependency = false;
1486
            p.mHasCircularDependency = false;
1487
            p.mBreadCrumb = 0;
1488
        }
1489
    }
6914 dpurdie 1490
 
1491
    /**
1492
     * Returns true is a part of a circular dependency
1493
     * Will examine all the packages sub dependencies and mark those that do have a
1494
     * circular dependency.
1495
     * 
1496
     * This process works by descending the dependency tree and dropping a bread crumb
1497
     * If the bread crumb is seen during the decent, then a circle has been detected and
1498
     * the package (with the bread crumb) will be marked as having a circular dependency
1499
     * 
1500
     *  Assumes that the caller will walk ALL packages and flag those with a circular
1501
     *  dependence AND those that depend on that package.
1502
     * 
1503
     */
7078 dpurdie 1504
    private boolean detectCircularDependency(String alias, ArrayList<Package> packageCollection, Package parent)
6914 dpurdie 1505
    {
1506
        mLogger.debug("detectCircularDependency");
1507
        boolean retVal = false;
1508
 
1509
        // if this package has yet to be checked for circular dependency
1510
        if (!mCheckedCircularDependency)
1511
        {
1512
            // Will be set as we drill down through dependencies
1513
            // If we see this marker (bread crumb) then we have a loop
1514
            if (mBreadCrumb != 0)
1515
            {
1516
                mBreadCrumb = 3;
1517
 
1518
                mHasCircularDependency = true;
1519
                if(parent != null && parent.mBreadCrumb != 3 )
1520
                {
1521
                    parent.mBreadCrumb = 2;
1522
                }
1523
            }
1524
            else
1525
            {
1526
                // Mark this package as potentially having a circular dependency
1527
                // Will now drill down and see if we hit a marker
1528
                mBreadCrumb = 1;
1529
 
1530
                // Recurse down the dependencies and sub dependencies
1531
                for (Iterator<String> it2 = mDependencyCollection.iterator(); it2.hasNext();)
1532
                {
1533
                    String dependencyAlias = it2.next();
7078 dpurdie 1534
 
1535
                    // Scan the collection looking for the specified package alias
1536
                    for (Iterator<Package> it = packageCollection.iterator(); it.hasNext();)
1537
                    {
1538
                        Package dependency = it.next();
1539
 
1540
                        if ( dependency.mAlias.compareTo( dependencyAlias ) == 0 )
1541
                        {
1542
                            dependency.detectCircularDependency(alias, packageCollection, this);
1543
                            break;
1544
                        }
1545
                    }
6914 dpurdie 1546
                }
1547
 
1548
                if (mBreadCrumb == 2)
1549
                {
1550
                    mHasCircularDependency = true;
1551
                    if(parent != null && parent.mBreadCrumb != 3 )
1552
                    {
1553
                        parent.mBreadCrumb = 2;
1554
                    }
1555
                }
1556
                mBreadCrumb = 0;
1557
            }
1558
 
1559
            // Flag package as having been examined
1560
            mCheckedCircularDependency = true;
1561
        } 
1562
 
1563
        // return the persisted circular dependency outcome
1564
        retVal = mHasCircularDependency;
7048 dpurdie 1565
        mLogger.info("detectCircularDependency 2 returned {}", retVal);
6914 dpurdie 1566
        return retVal;
1567
    }
7048 dpurdie 1568
 
1569
    /** Set the sequence of all packages in the list
1570
     *  Used so that the Unit Tests function can preserve the basic ordering of the list 
1571
     */
1572
    public static void setSequence(ArrayList<Package> al)
1573
    {
1574
        int seq = 1;
1575
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1576
        {
1577
            Package p = it.next();
1578
            p.mSeqId = seq++;
1579
        }
1580
    }
6914 dpurdie 1581
 
7070 dpurdie 1582
    /** Reset the processed flag on a collection of packages
1583
     * 
1584
     */
1585
    public static void resetProcessed (ArrayList<Package> al)
1586
    {
1587
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1588
        {
1589
            Package p = it.next();
1590
            p.mIsProcessed = false;
7078 dpurdie 1591
            p.mProcessed = false;
1592
            if (p.mBuildFile > 0)
1593
                p.mBuildFile = 0;
1594
            p.mRippleOrder = 0;
7070 dpurdie 1595
        }
1596
    }
7048 dpurdie 1597
 
7070 dpurdie 1598
    /** Reset the rippleOrder flag on a collection of packages
1599
     * 
1600
     */
1601
    public static void resetRippleOrder (ArrayList<Package> al)
1602
    {
1603
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1604
        {
1605
            Package p = it.next();
1606
            p.mRippleOrder = 0;
1607
        }
1608
    }   
1609
 
7048 dpurdie 1610
    /** Comparator for sorting package collections by mSeqId
1611
     *  Used to preserve order for unit testing
1612
     */
1613
    public static final Comparator<Package> SeqComparator = new Comparator<Package>() {
1614
 
1615
        /**
1616
         * Returns -ve: p1 is less than p2
1617
         *           0: p1 = p2
1618
         *         +ve: p1 > p2
1619
         */
1620
        public int compare (Package p1, Package p2) {
1621
                return p1.mSeqId - p2.mSeqId;
1622
        }
7070 dpurdie 1623
    };
1624
 
7078 dpurdie 1625
    /** Comparator for sorting package collections by mBuildTime and mRippleOrder
7070 dpurdie 1626
     *  Preserve rippleOrder
1627
     *  
1628
     */
7078 dpurdie 1629
    public static final Comparator<Package> BuildOrderComparitor = new Comparator<Package>() {
7070 dpurdie 1630
 
1631
        /**
1632
         * Returns -ve: p1 is less than p2
1633
         *           0: p1 = p2
1634
         *         +ve: p1 > p2
1635
         */
1636
        public int compare (Package p1, Package p2) {
1637
            if (p1.mRippleOrder == p2.mRippleOrder)
1638
            {
7078 dpurdie 1639
                return p1.mBuildTime - p2.mBuildTime;
7070 dpurdie 1640
            }
1641
 
1642
            return p1.mRippleOrder - p2.mRippleOrder;
1643
 
1644
        }
1645
    };
1646
 
6914 dpurdie 1647
    /**
1648
     * entity class supporting the ERG version numbering standard:
1649
     * <major>.<minor>.<patch/build> patch/build is at least a 4 digit number
1650
     * whose last 3 digits represent the build
1651
     */
1652
    public class VersionNumberingStandard
1653
    {
1654
        /**
1655
         * in terms of the mChangeType Package field, when true indicates the
1656
         * contract of the package has changed in a non backwardly compatible
1657
         * manner in terms of the mRippleField Package field, when true indicates
1658
         * the major version number will be incremented
1659
         * 
1660
         * @attribute
1661
         */
1662
        private boolean mMajor = false;
1663
 
1664
        /**
1665
         * in terms of the mChangeType Package field, when true indicates the
1666
         * contract of the package has changed in a backwardly compatible manner
1667
         * in terms of the mRippleField Package field, when true indicates the
1668
         * minor version number will be incremented
1669
         * 
1670
         * @attribute
1671
         */
1672
        private boolean mMinor = false;
1673
 
1674
        /**
1675
         * in terms of the mChangeType Package field, when true indicates the
1676
         * contract of the package has not changed, but the package has changed
1677
         * internally in terms of the mRippleField Package field, when true
1678
         * indicates the minor version number will be incremented
1679
         * 
1680
         * @attribute
1681
         */
1682
        private boolean mPatch = false;
1683
 
1684
        /**
1685
         * in terms of the mChangeType Package field, when true indicates the
1686
         * package has not changed, its dependencies potentially have in terms
1687
         * of the mRippleField Package field, when true indicates the build
1688
         * number will be incremented
1689
         * 
1690
         * @attribute
1691
         */
1692
        private boolean mBuild = true;
1693
 
1694
        /**
1695
         * in terms of the mChangeType Package field, when true indicates the
1696
         * major, minor, and patch number will be incremented according to field
1697
         * limits in terms of the mRippleField Package field, when true indicates
1698
         * the major, minor, patch and build number will be incremented
1699
         * according to field limits
1700
         * 
1701
         * @attribute
1702
         */
1703
        private boolean mLimit = false;
1704
 
1705
        /**
1706
         * in terms of the mChangeType Package field, when true indicates the
1707
         * package version number will not be rippled. The user will have fixed
1708
         * the version number. This is only application to WIP packages
1709
         * 
1710
         * @attribute
1711
         */
1712
        private boolean mFixed = false;
1713
 
1714
        /**
1715
         * in terms of the mChangeType Package field, when true indicates the
1716
         * method of rippling a package version number is not known.
1717
         * 
1718
         * @attribute
1719
         */
1720
        private boolean mUnknown = false;
1721
 
1722
        /**
1723
         * constructor
1724
         */
1725
        private VersionNumberingStandard()
1726
        {
1727
            mLogger.debug("VersionNumberingStandard");
1728
        }
1729
 
1730
        /**
1731
         * Reset all values to a known state
1732
         * 
1733
         */
1734
        void resetData()
1735
        {
1736
            mBuild = false;
1737
            mMajor = false;
1738
            mMinor = false;
1739
            mPatch = false;
1740
            mLimit = false;
1741
            mFixed = false;
1742
            mUnknown = false;
1743
        }
1744
 
1745
        /**
1746
         * sets mBuild true, mMajor false, mMinor false, mPatch false, mLimit
1747
         * false
1748
         */
1749
        void setBuild()
1750
        {
1751
            mLogger.debug("setBuild");
1752
            resetData();
1753
            mBuild = true;
1754
        }
1755
 
1756
        /**
1757
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1758
         * false
1759
         */
1760
        void setMajor()
1761
        {
1762
            mLogger.debug("setMajor");
1763
            resetData();
1764
            mMajor = true;
1765
        }
1766
 
1767
        /**
1768
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1769
         * limit
1770
         */
1771
        void setMajor(boolean limit)
1772
        {
1773
            mLogger.debug("setMajor " + limit);
1774
            resetData();
1775
            mMajor = true;
1776
            mLimit = limit;
1777
        }
1778
 
1779
        /**
1780
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1781
         * false
1782
         */
1783
        void setMinor()
1784
        {
1785
            mLogger.debug("setMinor");
1786
            resetData();
1787
            mMinor = true;
1788
        }
1789
 
1790
        /**
1791
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1792
         * limit
1793
         */
1794
        void setMinor(boolean limit)
1795
        {
1796
            mLogger.debug("setMinor " + limit);
1797
            resetData();
1798
            mMinor = true;
1799
            mLimit = limit;
1800
        }
1801
 
1802
        /**
1803
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1804
         * false
1805
         */
1806
        void setPatch()
1807
        {
1808
            mLogger.debug("setPatch");
1809
            resetData();
1810
            mPatch = true;
1811
        }
1812
 
1813
        /**
1814
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1815
         * limit
1816
         */
1817
        void setPatch(boolean limit)
1818
        {
1819
            mLogger.debug("setPatch");
1820
            resetData();
1821
            mPatch = true;
1822
            mLimit = limit;
1823
        }
1824
 
1825
        /**
1826
         * sets mBuild false, mMajor false, mMinor false, mPatch false, mLimit
1827
         * true
1828
         */
1829
        void setLimit()
1830
        {
1831
            mLogger.debug("setPatch");
1832
            resetData();
1833
            mLimit = true;
1834
        }
1835
 
1836
        /**
1837
         * sets parameters to indicate that the change type is Fixed. The
1838
         * version number is set by the user and a ripple will not be calculated
1839
         */
1840
        void setFixed()
1841
        {
1842
            mLogger.debug("setFixed");
1843
            resetData();
1844
            mFixed = true;
1845
        }
1846
 
1847
        /**
1848
         * Sets parameters to indicate that the change type is not known
1849
         * 
1850
         */
1851
        void setUnknown()
1852
        {
1853
            resetData();
1854
            mUnknown = true;
1855
        }
1856
 
1857
    }
1858
 
7070 dpurdie 1859
    /**
1860
     *  Add a package dependency
1861
     *  UTF Use
1862
     *  @param p - The package to add as a dependent
1863
     *  @return - The current package to allow chaining of calls
1864
     */
1865
    public Package addDependency(Package p) {
1866
        mDependencyCollection.add(p.mAlias);
1867
        mDependencyIDCollection.add(p.mId);
1868
        return this;
1869
    }
1870
 
7071 dpurdie 1871
    /** Clear the packages list of dependencies
1872
     *  UTF use
1873
     *  @return - The current package to allow chaining of calls
1874
     */
1875
    public Package resetDependencies() {
1876
        mDependencyCollection.clear();
1877
        mDependencyIDCollection.clear();
1878
        return this;
1879
    }
1880
 
6914 dpurdie 1881
}