Subversion Repositories DevTools

Rev

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

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