Subversion Repositories DevTools

Rev

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