VPP/ArtifactVersioning

From fd.io
< VPP
Jump to: navigation, search


DEB artifact versions, in DEB sort order applied,
before change Ib69a59ee98aa296c5cb24b183bba807b6cb3c05c:


19.08.2-release              <--- in fdio/release
19.08.2-2~g220e40541~b2500       |
19.08.2-1~g99fa5f722~b2499       | <--- fdio/1908
19.08.1-release               <--- in fdio/release
19.08.1-402~g0830008b3~b2497     |
19.08.1-401~g8e4ed521a~b2496     |
...
19.08.1-3~g1ffabcb3d~b2098       |
19.08.1-2~g6b9847e80~b2097       |
19.08.1-1~gda900b25c~b2096       |
19.08-rc2~11-g01685bead~b2067    |
19.08-rc2~10-g2ecbf6dc5~b2066    |
...
19.08-rc2~2-ga5766fe13~b2058     |
19.08-rc2~1-g4c945dacb~b2057     | <--- in fdio/1908
19.08-rc1~19-gbb83b16a3~b2055    |
19.08-rc1~18-g8ef151c90~b2054    |
...
19.08-rc1~2-gc7f0fe030~b2038     |
19.08-rc1~1-g7119c22f8~b2037   --/
19.08-rc0~840-gc0bc26eaf~b2035 ====\
19.08-rc0~839-g08f2a5dae~b2035     |
19.08-rc0~838-g388f418a8~b2033     | <=== in fdio/master
...
19.08-rc0~2-g7c91007e1~b1197       |
19.08-rc0~1-g10dc2eabd~b1196  =====/
19.08-release                  <--- in fdio/release
19.08-rc2~b2056                <--- in fdio/1908  overshadowed by post-rc1 but pre-rc2 builds, overshadows post-19.08 builds
19.08-rc1~b2036                <--- in fdio/1908  (the first build to go into fdio/1908) overshadows post-19.08 builds
19.08-rc0~b1195             <======== === in fdio/master
19.08-26~g7c33c343e~b2094     ---\
19.08-25~g50f883106~b2093        |
19.08-24~ge6a571262~b2092        | <--- in fdio/1908 (overshadowed by rc1/2 builds)
...
19.08-2~g9e25c7723~b2070         |
19.08-1~g46441cb9c~b2069       --/
19.04-rc0~674-g3f86ca3ee~b1194 ====\
19.04-rc0~673-ge36f44ad9~b1193     | <=== in fdio/master (before stable/1904 branch)
                              =====/


DEB artifact versions, in DEB sort order applied, after change:



19.08.2-release           <---------- in fdio/release
19.08.2-2~g220e40541~b2500   ----\
19.08.2-1~g99fa5f722~b2499    ---/ <-- in fdio/1908
19.08.1-release           <---------- in fdio/release
19.08.1-402~g0830008b3~b2497  ---\
19.08.1-401~g8e4ed521a~b2496     |
...
19.08.1-3~g1ffabcb3d~b2098       |
19.08.1-2~g6b9847e80~b2097       |
19.08.1-1~gda900b25c~b2096       |
19.08.0-26~g7c33c343e~b2094      |
19.08.0-25~g50f883106~b2093      |
19.08.0-24~ge6a571262~b2092      |   <---- in fdio/1908, all in correct order
...
19.08.0-2~g9e25c7723~b2070       |
19.08.0-1~g46441cb9c~b2069       |
19.08-rc2~11-g01685bead~b2067    |
19.08-rc2~10-g2ecbf6dc5~b2066    |
19.08-rc2~9-g14c7756ad~b2065     |
...
19.08-rc2~1-g4c945dacb~b2057     |
19.08-rc2~0-g2f51729bb~b2056  <------- in fdio/1908 previously was called "19.08-rc2", now in the correct place
19.08-rc1~19-gbb83b16a3~b2055    |
19.08-rc1~18-g8ef151c90~b2054    |
...
19.08-rc1~2-gc7f0fe030~b2038     |
19.08-rc1~1-g7119c22f8~b2037     |
19.08-rc1~0-g23526f78a~b2036  <--/---- in fdio/1908, previously was called "19.08-rc1", now in the correct place
19.08-rc0~840-gc0bc26eaf~b2035 ====\
19.08-rc0~839-g08f2a5dae~b2034     | <=== in fdio/master
...
19.08-rc0~1-g10dc2eabd~b1196       |
19.08-rc0~0-g40fd1f3df~b1195 ======/
19.08-release                   <---- in fdio/release
19.04-rc0~674-g3f86ca3ee~b1194  <========  in fdio/master (before stable/1904 branch)
19.04-rc0~673-ge36f44ad9~b1193  <=======/

The code used to test was as follows:

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>


 static int order(char c)
 {
    if (isdigit(c))
       return 0;
    else if (isalpha(c))
       return c;
    else if (c == '~')
       return -1;
    else if (c)
       return c + 256;
    else
       return 0;
 }

 int CmpFragment(const char *A,const char *AEnd,
                      const char *B,const char *BEnd)
 {
    /* Iterate over the whole string
       What this does is to split the whole string into groups of
       numeric and non numeric portions. For instance:
          a67bhgs89
       Has 4 portions 'a', '67', 'bhgs', '89'. A more normal:
          2.7.2-linux-1
       Has '2', '.', '7', '.' ,'-linux-','1' */
    const char *lhs = A;
    const char *rhs = B;
    while (lhs != AEnd && rhs != BEnd)
    {
       int first_diff = 0;

       while (lhs != AEnd && rhs != BEnd &&
          (!isdigit(*lhs) || !isdigit(*rhs)))
       {
      int vc = order(*lhs);
      int rc = order(*rhs);
      if (vc != rc)
         return vc - rc;
      ++lhs; ++rhs;
       }

       while (*lhs == '0')
      ++lhs;
       while (*rhs == '0')
      ++rhs;
       while (isdigit(*lhs) && isdigit(*rhs))
       {
      if (!first_diff)
         first_diff = *lhs - *rhs;
      ++lhs;
      ++rhs;
       }

       if (isdigit(*lhs))
      return 1;
       if (isdigit(*rhs))
      return -1;
       if (first_diff)
      return first_diff;
    }

    // The strings must be equal
    if (lhs == AEnd && rhs == BEnd)
       return 0;

    // lhs is shorter
    if (lhs == AEnd)
    {
       if (*rhs == '~') return 1;
       return -1;
    }

    // rhs is shorter
    if (rhs == BEnd)
    {
       if (*lhs == '~') return -1;
       return 1;
    }

    // Shouldn't happen
    return 1;
 }



/* This fragments the version into E:V-R triples and compares each 
   portion separately. */
int DoCmpVersion(char *A,char *AEnd,
                                       char *B, char *BEnd)
{
   // Strip off the epoch and compare it
    char *lhs = ( char*) memchr(A, ':', AEnd - A);
    char *rhs = ( char*) memchr(B, ':', BEnd - B);
   if (lhs == NULL)
      lhs = A;
   if (rhs == NULL)
      rhs = B;

   // Special case: a zero epoch is the same as no epoch,
   // so remove it.
   if (lhs != A)
   {
      for (; *A == '0'; ++A);
      if (A == lhs)
      {
         ++A;
         ++lhs;
      }
   }
   if (rhs != B)
   {
      for (; *B == '0'; ++B);
      if (B == rhs)
      {
         ++B;
         ++rhs;
      }
   }

   // Compare the epoch
   int Res = CmpFragment(A,lhs,B,rhs);
   if (Res != 0)
      return Res;

   // Skip the :
   if (lhs != A)
      lhs++;
   if (rhs != B)
      rhs++;

   // Find the last -
    char *dlhs = ( char*) memrchr(lhs, '-', AEnd - lhs);
    char *drhs = ( char*) memrchr(rhs, '-', BEnd - rhs);
   if (dlhs == NULL)
      dlhs = AEnd;
   if (drhs == NULL)
      drhs = BEnd;

   // Compare the main version
   Res = CmpFragment(lhs,dlhs,rhs,drhs);
   if (Res != 0)
      return Res;

   // Skip the -
   if (dlhs != lhs)
      dlhs++;
   if (drhs != rhs)
      drhs++;

   // no debian revision need to be treated like -0
   if (*(dlhs-1) == '-' && *(drhs-1) == '-')
      return CmpFragment(dlhs,AEnd,drhs,BEnd);
   else if (*(dlhs-1) == '-')
   {
       char* null = "0";
      return CmpFragment(dlhs,AEnd,null, null+1);
   }
   else if (*(drhs-1) == '-')
   {
       char* null = "0";
      return CmpFragment(null, null+1, drhs, BEnd);
   }
   else
      return 0;
}

char cmp_a[1024];
char cmp_b[1024];

int deb_compare(char **a, char **b) {
	int len_a = strlen(*a);
	memcpy(cmp_a, *a, len_a+1);
	char *end_a = cmp_a + len_a;

	int len_b = strlen(*b);
	memcpy(cmp_b, *b, len_b+1);
	char *end_b = cmp_b + len_b;
	int res = -DoCmpVersion(cmp_a, end_a, cmp_b, end_b);
	// printf("A: '%s' B: '%s' res:%d\n", cmp_a, cmp_b, res);
	return res;
}


#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* This was lifted pretty much verbatim from
 * https://fastapi.metacpan.org/source/HAG/RPM-VersionSort-1.00/VersionSort.xs */

/* This is lifted pretty much verbatim from the RPM 4.0.2 sources.
   See lib/misc.c
*/

/* Note that the semantics as documented by redhat are incorrect: some
   return value semantics are inherited from strcmp, which only promises
   to use integers greater, less, or equal to zero, which is not the same
   as (-1,0,1).  So we fix this up in the xsub.
*/

/* compare alpha and numeric segments of two versions */
/* return 1: a is newer than b */
/*        0: a and b are the same version */
/*       -1: b is newer than a */


int rpmvercmp(const char * a, const char * b)
{
    char oldch1, oldch2;
    char * str1, * str2;
    char * one, * two;
    int rc;
    int isnum;

    /* easy comparison to see if versions are identical */
    if (!strcmp(a, b)) return 0;

    str1 = alloca(strlen(a) + 1);
    str2 = alloca(strlen(b) + 1);

    strcpy(str1, a);
    strcpy(str2, b);

    one = str1;
    two = str2;

    /* loop through each version segment of str1 and str2 and compare them */
    while (*one && *two) {
	while (*one && !isalnum(*one)) one++;
	while (*two && !isalnum(*two)) two++;

	str1 = one;
	str2 = two;

	/* grab first completely alpha or completely numeric segment */
	/* leave one and two pointing to the start of the alpha or numeric */
	/* segment and walk str1 and str2 to end of segment */
	if (isdigit(*str1)) {
	    while (*str1 && isdigit(*str1)) str1++;
	    while (*str2 && isdigit(*str2)) str2++;
	    isnum = 1;
	} else {
	    while (*str1 && isalpha(*str1)) str1++;
	    while (*str2 && isalpha(*str2)) str2++;
	    isnum = 0;
	}

	/* save character at the end of the alpha or numeric segment */
	/* so that they can be restored after the comparison */
	oldch1 = *str1;
	*str1 = '\0';
	oldch2 = *str2;
	*str2 = '\0';

	/* take care of the case where the two version segments are */
	/* different types: one numeric and one alpha */
	if (one == str1) return -1;	/* arbitrary */
	if (two == str2) return -1;

	if (isnum) {
	    /* this used to be done by converting the digit segments */
	    /* to ints using atoi() - it's changed because long  */
	    /* digit segments can overflow an int - this should fix that. */

	    /* throw away any leading zeros - it's a number, right? */
	    while (*one == '0') one++;
	    while (*two == '0') two++;

	    /* whichever number has more digits wins */
	    if (strlen(one) > strlen(two)) return 1;
	    if (strlen(two) > strlen(one)) return -1;
	}

	/* strcmp will return which one is greater - even if the two */
	/* segments are alpha or if they are numeric.  don't return  */
	/* if they are equal because there might be more segments to */
	/* compare */
	rc = strcmp(one, two);
	if (rc) return rc;

	/* restore character that was replaced by null above */
	*str1 = oldch1;
	one = str1;
	*str2 = oldch2;
	two = str2;
    }

    /* this catches the case where all numeric and alpha segments have */
    /* compared identically but the segment sepparating characters were */
    /* different */
    if ((!*one) && (!*two)) return 0;

    /* whichever version still has characters left over wins */
    if (!*one) return -1; else return 1;
}

int rpm_compare(const char **a, const char **b)
    {
	int vercmp = rpmvercmp(*a, *b);
	int result = 0;
	if(vercmp < 0) result = 1;
	if(vercmp > 0) result = -1;
	// printf("RPM comp '%s' vs '%s': %d\n", *a, *b, result);
	return result;
    }



int main(int argc, char **argv)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    char **versions = malloc(1024000);
    int n_versions  = 0;

    if (argc < 2) {
            fp = fopen("versions.txt", "r");
    } else {
            fp = fopen(argv[1], "r");
    }
    if (fp == NULL)
        exit(EXIT_FAILURE);

    int i;

    while ((read = getline(&versions[n_versions], &len, fp)) != -1) {
	line = versions[n_versions];
	if (strlen(line) > 0) {
		line[strlen(line)-1] = 0;
	}
	if (strlen(line) == 0) {
		break;
	}
	n_versions++;
    }
/*
    printf("Original list:\n");
    for (i=0; i<n_versions; i++) {
	    printf("%s\n", versions[i]);
    }
    */


    qsort(versions, n_versions, sizeof(char *), deb_compare);

    printf("\nDEB sorting:\n");
    for (i=0; i<n_versions; i++) {
	    printf("%s\n", versions[i]);
    }

    qsort(versions, n_versions, sizeof(char *), rpm_compare);
    printf("\nRPM sorting:\n");

    for (i=0; i<n_versions; i++) {
	    printf("%s\n", versions[i]);
    }

    fclose(fp);
    exit(EXIT_SUCCESS);
}