Subversion Repositories DevTools

Rev

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