Subversion Repositories DevTools

Rev

Rev 868 | Rev 888 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.erggroup.buildtool.ripple;

import java.io.File;

import java.util.Iterator;
import java.util.Vector;

import org.apache.log4j.Logger;

public class Package
{
  /**name of package, must not contain spaces
   * @attribute
   */
  String mName = new String();

  /**package scope
   * @attribute
   */
  String mExtension = new String();

  /**instance identifier
   * @attribute
   */
  String mVersion = new String();

  /**unique identifier
   * for daemon builds = mName + mExtension
   * for escrow builds = mName + mVersion + mExtension
   * @attribute
   */
  String mAlias = new String();

  /**clearcase vob location, must not contain spaces
   * @attribute
   */
  String mLocation = new String();

  /**clearcase source file instance identifier
   * @attribute
   */
  String mLabel = new String();

  /**build standards
   * @attribute
   */
  Vector<BuildStandard> mBuildStandardCollection = new Vector<BuildStandard>();

  /**GBE_MACHTYPE used to build generic packages for this baseline
   * only has meaning in the daemon build, not the escrow build
   * accessed by BuildStandard::getPlatform, getBuildStandard
   * @attribute
   */
  public static final String mGenericMachtype = System.getenv("GBE_MACHTYPE");

  /**build dependencies by package alias
   * @attribute
   */
  Vector<String> mDependencyCollection = new Vector<String>();

  /**primary package version key pv_id in database
   * @attribute
   */
  int mId;

  /**indication of the nature of change
   * @attribute
   */
  Package.VersionNumberingStandard mChangeType = new VersionNumberingStandard();

  /**determines what field is rippled on a package version whose dependencies have changed
   * @attribute
   */
  Package.VersionNumberingStandard mRippleField = new VersionNumberingStandard();

  /**interested owners
   * @attribute
   */
  private Vector<String> mBuildFailureEmailCollection = new Vector<String>();

  /**when true will trigger unit tests as part of the package build phase in daemon mode
   * @attribute
   */
  boolean mHasAutomatedUnitTests = false;

  /**when true, do not ripple this package through packages which are dependent upon it in daemon mode
   * @attribute
   */
  boolean mAdvisoryRipple = false;

  /**determines the build file the package is built in, or not
   *  1 buildfile 1 etc
   *  0 not yet processed (initial value)
   * -1 not reproducible
   * -2 not reproducible on the build platforms configured for this release
   * -3 do not ripple
   * -4 directly dependent on package versions not in the baseline
   * -5 indirectly dependent on package versions which are not reproducible
   *    because of -1, -2 (escrow), -3 (daemon), -4
   * @attribute
   */
  int mBuildFile = 0;

  /**build dependencies by package
   * @attribute
   */
  Vector<Package> mPackageDependencyCollection = new Vector<Package>();

  /**used for escrow build purposes
   * set true when a package has been processed
   * @attribute
   */
  boolean mProcessed = false;

  /**set true for WIP package versions
   * only used in daemon mode
   * @attribute
   */
  boolean mDirectlyPlanned = false;

  /**set true when it is determined to be ripple built
   * @attribute
   */
  boolean mIndirectlyPlanned = false;

  /**build dependencies by pv_id (-1 or not used for planned dependencies)
   * @attribute
   */
  Vector<Integer> mDependencyIDCollection = new Vector<Integer>();

  /**unique pkg_id in the database
   * used for querying package version existence in the database in daemon mode
   * @attribute
   */
  int mPid;

  /**maximum major number supported for determining ripple number
   * @attribute
   */
  int mMajorLimit;

  /**maximum minor number supported for determining ripple number
   * @attribute
   */
  int mMinorLimit;

  /**maximum patch number supported for determining ripple number
   * @attribute
   */
  int mPatchLimit;

  /**maximum build number number supported for determining ripple number
   * @attribute
   */
  int mBuildLimit;

  /**Logger
   * @attribute
   */
  private static final Logger mLogger = Logger.getLogger(Package.class);

  /**dpkg archive location
   * @attribute
   */
  public static final String mGbeDpkg = System.getenv("GBE_DPKG");

  /**deploy archive location
   * @attribute
   */
  public static final String mGbeDply = System.getenv("GBE_DPLY");

  /**true if the package exists in the dpkg or deploy archive
   * @attribute
   */
  private boolean mArchivalExistence = true;

  /**when true will trigger source control interaction eg labelling
   * @attribute
   */
  public boolean mRequiresSourceControlInteraction = true;

  /**constructor
   */
  Package(int pv_id, String pkg_name, String v_ext, String alias, 
          String pkg_label, String src_path, char change_type)
  {
    mLogger.debug("Package 1: pv_id " + pv_id + " pkg_name " + pkg_name + " v_ext " + v_ext + " alias " + alias + " pkg_label " + pkg_label + " src_path " + src_path + " change_type " + change_type);
    mId = pv_id;
    mName = pkg_name;
    mVersion = "0.0.0000";
    mExtension = v_ext;
    mAlias = alias;
    mLabel = pkg_label;
    mLocation = src_path;
    
    if (change_type == 'M')
    {
      mChangeType.setMajor();
    }
    else if (change_type == 'N')
    {
      mChangeType.setMinor();
    }
    else
    {
      mChangeType.setPatch();
    }
  }

  /**constructor
   */
  Package(int pv_id, String pkg_name, String pkg_version, String v_ext, 
          String alias, String pkg_label, String src_path, 
          char ripple_field)
  {
    mLogger.debug("Package 2: pv_id " + pv_id + " pkg_name " + pkg_name + " pkg_version " + pkg_version + " v_ext " + v_ext + " alias " + alias + " pkg_label " + pkg_label + " src_path " + src_path + " ripple_field " + ripple_field);
    mId = pv_id;
    mName = pkg_name;
    mVersion = pkg_version;
    int endindex = mVersion.length() - v_ext.length();
    
    if ( endindex > 0 )
    {
      mVersion = mVersion.substring(0, endindex);
    }
    
    mExtension = v_ext;
    mAlias = alias;
    mLabel = pkg_label;
    mLocation = src_path;
    
    // setBuild is the default
    if (ripple_field == 'M')
    {
      mRippleField.setMajor();
    }
    else if (ripple_field == 'm')
    {
      mRippleField.setMinor();
    }
    else if (ripple_field == 'p')
    {
      mRippleField.setPatch();
    }
    else if (ripple_field == 'L')
    {
      mRippleField.setLimit();
    }
  }

  /**constructor
   */
  Package()
  {
    mLogger.debug("Package 3");
    mId = 0;
    mName = "null";
    mExtension = "null";
    mAlias = "null";
    mLabel = "null";
    mLocation = "null";
  }

  /**constructor for unit test purposes
  */
  public Package(ReleaseManager rm, String version, int majorLimit, int minorLimit, int patchLimit, int buildNumberLimit)
  {
    mId = -1;
    mRippleField.setLimit();
    mVersion = version;
    mMajorLimit = majorLimit;
    mMinorLimit = minorLimit;
    mPatchLimit = patchLimit;
    mBuildLimit = buildNumberLimit;
    try
    {
      mId = applyPV( rm, 0 );
    }
    catch(Exception e)
    {
    }
  }

  /**accessor for unit test purposes
   */
  public int getId()
  {
    return mId;
  }
  
  /**accessor for unit test purposes
   */
  public String getVersion()
  {
    return mVersion;
  }
  
  /**returns true if mBuildStandardCollection is not empty
   */
  boolean isReproducible()
  {
    mLogger.debug("isReproducible on Package " + mName);
    boolean retVal = false;
    
    if ( mBuildStandardCollection.size() > 0 )
    {
      retVal = true;
    }
    
    mLogger.info("isReproducible returned " + retVal);
    return retVal;
  }

  /**returns true if at least one of its BuildStandards has mWin32 or mGeneric true
   */
  boolean isWin32Built()
  {
    mLogger.debug("isWin32Built on Package " + mName);
    boolean retVal = false;
    for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext(); )
    {
      BuildStandard buildStandard = it.next();

      if (buildStandard.getWin32() || buildStandard.getGeneric())
      {
        retVal = true;
        break;
      }
    }

    mLogger.info("isWin32Built returned " + retVal);
    return retVal;
  }

  /**returns true if at least one of its BuildStandards has mSolaris or mGeneric true
   */
  boolean isSolarisBuilt()
  {
    mLogger.debug("isSolarisBuilt on Package " + mName);
    boolean retVal = false;
    for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext(); )
    {
      BuildStandard buildStandard = it.next();

      if (buildStandard.getSolaris() || buildStandard.getGeneric())
      {
        retVal = true;
        break;
      }
    }

    mLogger.info("isSolarisBuilt returned " + retVal);
    return retVal;
  }

  /**returns true if at least one of its BuildStandards has mLinux or mGeneric true
   */
  boolean isLinuxBuilt()
  {
    mLogger.debug("isLinuxBuilt on Package " + mName);
    boolean retVal = false;
    for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext(); )
    {
      BuildStandard buildStandard = it.next();

      if (buildStandard.getLinux() || buildStandard.getGeneric())
      {
        retVal = true;
        break;
      }
    }

    mLogger.info("isLinuxBuilt returned " + retVal);
    return retVal;
  }

  /**returns true if at least one of its BuildStandards has mGeneric true
   */
  boolean isGeneric()
  {
    mLogger.debug("isGeneric on Package " + mName);
    boolean retVal = false;
    for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext(); )
    {
      BuildStandard buildStandard = it.next();

      if (buildStandard.getGeneric())
      {
        retVal = true;
        break;
      }
    }

    mLogger.info("isGeneric returned " + retVal);
    return retVal;
  }

  /**applies the required version number change
   * returns 0 on success
   *         1 on cannot work with non standard versioning
   *         2 on ripple field limitations prevent a ripple build
   */
  int applyPV(ReleaseManager releaseManager, int rtag_id) throws Exception
  {
    mLogger.debug("applyPV on Package " + mName);
    // three scenarios, only applyPV for 2 of them
    // WIP exists:                      mDirectlyPlanned == true;   mIndirectlyPlanned == true; mArchivalExistence don't care - applyPV
    // Package version is out of date:  mDirectlyPlanned == false;  mIndirectlyPlanned == true; mArchivalExistence == true    - applyPV
    // Package version does not exist:  mDirectlyPlanned == false;  mIndirectlyPlanned == true; mArchivalExistence == false   - do not applyPV
    if ( !mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence )
    {
      // the package has an mIndirectlyPlanned flag set true in daemon mode because the package does not exist in an archive
      // do not apply a different package version
      mLogger.info("applyPV !mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence on Package " + mName);
      releaseManager.claimVersion(mPid, mVersion + mExtension, rtag_id);
      mLogger.info("applyPv returned 0");
      return 0;
    }
    
    int major = 0;
    int minor = 0;
    int patch = 1000;

    String field[] = mVersion.split("\\D");
    
    if ( field.length == 3 )
    {
      major = Integer.parseInt(field[0]);
      minor = Integer.parseInt(field[1]);
      patch = Integer.parseInt(field[2]);
    }
    else
    {
      // cannot work with non standard versioning
      mLogger.error("applyPV cannot work with non standard versioning");
      mLogger.info("applyPv returned 1");
      return 1;
    }

    if ( patch < 1000 && field[2].substring(0, 1).compareTo("0") != 0 )
    {
      mLogger.info("applyPV accomodate old style mVersion of the form 1.0.1");
      patch = patch * 1000;
    }
      
    // mChangeType overrides mRippleField
    do
    {
      if ( mChangeType.mMajor )
      {
        major++;
        mLogger.info("applyPV mChangeType.mMajor " + major);
        minor = 0;
        patch = 0;
      }
      else if ( mChangeType.mMinor )
      {
        minor++;
        mLogger.info("applyPV mChangeType.mMinor " + minor);
        patch = 0;
      }
      else if ( mChangeType.mPatch )
      {
        do
        {
          patch++;
        } while ( ( patch / 1000 ) * 1000 != patch );
        mLogger.info("applyPV mChangeType.mPatch " + patch);
      }
      else
      {
        if ( mRippleField.mMajor )
        {
          major++;
          mLogger.info("applyPV mRippleField.mMajor " + major);
          minor = 0;
          patch = 0;
        }
        else if ( mRippleField.mMinor )
        {
          minor++;
          mLogger.info("applyPV mRippleField.mMinor " + minor);
          patch = 0;
        }
        else if ( mRippleField.mPatch )
        {
          do
          {
            patch++;
          } while ( ( patch / 1000 ) * 1000 != patch );
          mLogger.info("applyPV mRippleField.mPatch " + patch);
        }
        else if ( mRippleField.mBuild )
        {
          patch++;
          mLogger.info("applyPV mRippleField.mBuild " + patch);
        }
        else
        {
          // increment fields according to limits
          boolean changeOccurred = false;
          boolean incrementField = true;
          
          if ( mBuildLimit != 0 )
          {
            // increment or reset the patch build number
            if ( patch < mBuildLimit )
            {
              // can increment the patch build number
              patch++;
              mLogger.info("applyPv mRippleField.mLimit build number " + patch);
              changeOccurred = true;
              incrementField = false;
            }
            else
            {
              if ( mPatchLimit == 0 )
              {
                // reset the patch number and patch build number
                patch = 0;
              }
            }
          }
          
          if ( mPatchLimit != 0 && incrementField )
          {
            // increment or reset the patch number
            if ( ( patch / 1000 ) < mPatchLimit )
            {
              do
              {
                patch++;
              } while ( ( patch / 1000 ) * 1000 != patch );
              
              mLogger.info("applyPv mRippleField.mLimit patch " + patch);
              changeOccurred = true;
              incrementField = false;
            }
            else
            {
              // reset the patch number and patch build number
              patch = 0;
            }
          }
          
          if ( mMinorLimit != 0 && incrementField )
          {
            // increment or reset the minor number
            if ( minor < mMinorLimit )
            {
              minor++;
              mLogger.info("applyPv mRippleField.mLimit minor " + minor);
              changeOccurred = true;
              incrementField = false;
            }
            else
            {
              // reset the minor number
              minor = 0;
            }
          }
          
          if ( mMajorLimit != 0 && incrementField )
          {
            // increment or reset the major number
            if ( major < mMajorLimit )
            {
              // increment the major number
              changeOccurred = true;
              major++;
              mLogger.info("applyPv mRippleField.mLimit major " + major);
            }
          }
          
          if ( !changeOccurred )
          {
            // unable to increment a field due to field limitations
            mLogger.error("applyPV ripple field limitations prevent a ripple build");
            mLogger.info("applyPv returned 2");
            return 2;
          }
        }
      }
      
      mVersion = String.valueOf(major) + "." + String.valueOf(minor) + ".";
      
      if ( patch < 10 )
      {
        mVersion += "000";
      }
      else if ( patch < 100 )
      {
        mVersion += "00";
      }
      else if ( patch < 1000 )
      {
        mVersion += "0";
      }
      
      mVersion += String.valueOf(patch);
    } while ( exists(releaseManager, rtag_id) );
    
    releaseManager.claimVersion(mPid, mVersion + mExtension, rtag_id);
    mLogger.info("applyPv returned 0");
    return 0;
  }

  /**returns true if the version exists in the dpkg_archive, deploy_archive or release manager database
   * claims the version in the release manager database
   */
  private boolean exists(ReleaseManager releaseManager, int rtag_id) throws Exception
  {
    mLogger.debug("exists 1 on Package " + mName + " version " + mVersion + " extension " + mExtension);
    boolean retVal = false;
    
    if ( !ReleaseManager.mUseDatabase )
    {
      mLogger.info("exists 1 !releaseManager.mUseDatabase");
    }
    else
    {
      retVal = exists();

      if ( !retVal )
      {
        String pkg_version = new String(mVersion);
        
        if ( mExtension.length() > 0 )
        {
          pkg_version += mExtension;
        }
        
        retVal = releaseManager.queryPackageVersions(mPid, pkg_version);
      }
    }
    
    mLogger.info("exists 1 returned " + retVal);
    return retVal;
  }

  /**returns true if the version exists in the dpkg_archive or deploy_archive
   */
  boolean exists()
    throws Exception
  {
    mLogger.debug("exists 2 on Package " + mName);
    boolean retVal = false;

    String Release = mGbeDpkg;
    String Deploy = mGbeDply;

    if (Release == null || Deploy == null)
    {
      mLogger.fatal("exists 2 Release == null || Deploy == null");
      throw new Exception("exists 2 Release == null || Deploy == null");
    }

    String fs = System.getProperty( "file.separator" );
    String name = new String(Release);
    name += fs + mName + fs + mVersion + mExtension;
    File release = new File(name);

    if (release.exists())
    {
      mLogger.info("exists 2 release.exists()");
      retVal = true;
    }

    if (!retVal && (Release != Deploy))
    {
      name = Deploy + fs + mName + fs + mVersion + mExtension;

      File deploy = new File(name);

      if (deploy.exists())
      {
        mLogger.info("exists 2 deploy.exists()");
        retVal = true;
      }
    }

    mArchivalExistence = retVal;
    mLogger.info("exists 2 returned " + retVal);
    return retVal;
  }
  
  /**returns email information
   */
  String emailInfo( String lf )
  {
    String retVal = new String();
    
    for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext(); )
    {
      String email = it.next();
      retVal +=
      "  <owner email=\"" + email +"\"/>" + lf;
    }
    
    return retVal;
  }

  /**returns email information
   */
  String emailInfoNonAntTask()
  {
    String retVal = null;
    
    for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext(); )
    {
      String email = it.next();
      
      if ( retVal == null )
      {
        retVal = new String();
      }
      else
      {
        retVal += ",";
      }
      retVal += email;
    }
    
    return retVal;
  }

  /**adds email to mBuildFailureEmailCollection if unique
   */
  void addEmail( String email )
  {
    boolean alreadyExists = false;
    
    for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext(); )
    {
      String existingEmail = it.next();
      
      if ( existingEmail.compareTo(email) == 0 )
      {
        alreadyExists = true;
        break;
      }
    }
    
    if ( !alreadyExists )
    {
      mBuildFailureEmailCollection.add(email);
    }
  }

  /**entity class supporting the ERG version numbering standard:
   * <major>.<minor>.<patch/build>
   * patch/build is at least a 4 digit number whose last 3 digits represent the build
   */
  public class VersionNumberingStandard
  {
    /**in terms of the mChangeType Package field,
     * when true indicates the contract of the package has changed in a non backwardly compatible manner
     * in terms of the mRippleField Package field,
     * when true indicates the major version number will be incremented
     * @attribute
     */
    private boolean mMajor = false;

    /**in terms of the mChangeType Package field,
     * when true indicates the contract of the package has changed in a backwardly compatible manner
     * in terms of the mRippleField Package field,
     * when true indicates the minor version number will be incremented
     * @attribute
     */
    private boolean mMinor = false;

    /**in terms of the mChangeType Package field,
     * when true indicates the contract of the package has not changed, but the package has changed internally
     * in terms of the mRippleField Package field,
     * when true indicates the minor version number will be incremented
     * @attribute
     */
    private boolean mPatch = false;

    /**in terms of the mChangeType Package field,
     * when true indicates the package has not changed, its dependencies potentially have
     * in terms of the mRippleField Package field,
     * when true indicates the build number will be incremented
     * @attribute
     */
    private boolean mBuild = true;

    /**in terms of the mChangeType Package field,
     * always false, does not apply
     * in terms of the mRippleField Package field,
     * when true indicates the major, minor, patch and build number will be incremented according to field limits
     * @attribute
     */
    private boolean mLimit = false;

    /**constructor
     */
    private VersionNumberingStandard()
    {
      mLogger.debug("VersionNumberingStandard");
    }

    /**sets mBuild true, mMajor false, mMinor false, mPatch false, mLimit false
     */
    void setBuild()
    {
      mLogger.debug("setBuild");
      mBuild = true;
      mMajor = false;
      mMinor = false;
      mPatch = false;
      mLimit = false;
    }

    /**sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit false
     */
    void setMajor()
    {
      mLogger.debug("setMajor");
      mBuild = false;
      mMajor = true;
      mMinor = false;
      mPatch = false;
      mLimit = false;
    }

    /**sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit false
     */
    void setMinor()
    {
      mLogger.debug("setMinor");
      mBuild = false;
      mMajor = false;
      mMinor = true;
      mPatch = false;
      mLimit = false;
    }

    /**sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit false
     */
    void setPatch()
    {
      mLogger.debug("setPatch");
      mBuild = false;
      mMajor = false;
      mMinor = false;
      mPatch = true;
      mLimit = false;
    }

    /**sets mBuild false, mMajor false, mMinor false, mPatch false, mLimit true
     */
    void setLimit()
    {
      mLogger.debug("setPatch");
      mBuild = false;
      mMajor = false;
      mMinor = false;
      mPatch = false;
      mLimit = true;
    }

  }

}