Subversion Repositories DevTools

Rev

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