updaterevision/updaterevision.c

changeset 807
2a8889692c25
parent 774
8bce7acd100c
child 861
83426c5fa732
equal deleted inserted replaced
806:4240f47aa2d4 807:2a8889692c25
1 /* updaterevision.c 1 /* updaterevision.c
2 * 2 *
3 * Public domain. This program uses git commands command to get 3 * Public domain. This program uses the svnversion command to get the
4 * various bits of repository status for a particular directory 4 * repository revision for a particular directory and writes it into
5 * and writes it into a header file so that it can be used for a 5 * a header file so that it can be used as a project's build number.
6 * project's versioning.
7 */ 6 */
8 7
9 #define _CRT_SECURE_NO_DEPRECATE 8 #define _CRT_SECURE_NO_DEPRECATE
10 9
11 #include <stdio.h> 10 #include <stdio.h>
12 #include <stdlib.h> 11 #include <stdlib.h>
13 #include <string.h> 12 #include <string.h>
14 #include <ctype.h> 13 #include <ctype.h>
15 #include <errno.h> 14 #include <errno.h>
15 // [BB] New #includes.
16 #include <sys/stat.h>
16 #include <time.h> 17 #include <time.h>
17
18 #ifdef _WIN32
19 #define popen _popen
20 #define pclose _pclose
21 #endif
22
23 // Used to strip newline characters from lines read by fgets.
24 void stripnl(char *str)
25 {
26 if (*str != '\0')
27 {
28 size_t len = strlen(str);
29 if (str[len - 1] == '\n')
30 {
31 str[len - 1] = '\0';
32 }
33 }
34 }
35 18
36 int main(int argc, char **argv) 19 int main(int argc, char **argv)
37 { 20 {
38 char vertag[128], lastlog[128], lasthash[128], *hash = NULL; 21 char *name;
22 char currev[64], lastrev[64], run[256], *rev;
23 unsigned long urev;
39 FILE *stream = NULL; 24 FILE *stream = NULL;
40 int gotrev = 0, needupdate = 1; 25 int gotrev = 0, needupdate = 1;
41 vertag[0] = '\0'; 26 // [BB] Are we working with a SVN checkout?
42 lastlog[0] = '\0'; 27 int svnCheckout = 0;
28 char hgdateString[64];
29 time_t hgdate = 0;
30 char hgHash[13];
31 hgHash[0] = '\0';
43 32
44 if (argc != 2) 33 if (argc != 3)
45 { 34 {
46 fprintf(stderr, "Usage: %s <path to gitinfo.h>\n", argv[0]); 35 fprintf (stderr, "Usage: %s <repository directory> <path to svnrevision.h>\n", argv[0]);
47 return 1; 36 return 1;
48 } 37 }
49 38
50 // Use git describe --tags to get a version string. If we are sitting directly 39 // [BB] Try to figure out whether this is a SVN or a Hg checkout.
51 // on a tag, it returns that tag. Otherwise it returns <most recent tag>-<number of
52 // commits since the tag>-<short hash>.
53 // Use git log to get the time of the latest commit in ISO 8601 format and its full hash.
54 stream = popen("git describe --tags && git log -1 --format=%ai*%H", "r");
55
56 if (NULL != stream)
57 { 40 {
58 if (fgets(vertag, sizeof vertag, stream) == vertag && 41 struct stat st;
59 fgets(lastlog, sizeof lastlog, stream) == lastlog) 42 char filename[1024];
60 { 43 sprintf ( filename, "%s/.svn/entries", argv[1] );
61 stripnl(vertag); 44 if ( stat ( filename, &st ) == 0 )
62 stripnl(lastlog); 45 svnCheckout = 1;
63 gotrev = 1; 46 // [BB] If stat failed we have to manually clear errno, otherwise the code below doesn't work.
64 } 47 else
65 48 errno = 0;
66 pclose(stream);
67 } 49 }
68 50
69 if (gotrev) 51 // Use svnversion to get the revision number. If that fails, pretend it's
52 // revision 0. Note that this requires you have the command-line svn tools installed.
53 // [BB] Depending on whether this is a SVN or Hg checkout we have to use the appropriate tool.
54 if ( svnCheckout )
55 sprintf (run, "svnversion -cn %s", argv[1]);
56 else
57 sprintf (run, "hg identify -n");
58 if ((name = tempnam(NULL, "svnout")) != NULL)
70 { 59 {
71 hash = strchr(lastlog, '*'); 60 #ifdef __APPLE__
72 if (hash != NULL) 61 // tempnam will return errno of 2 even though it is successful for our purposes.
62 errno = 0;
63 #endif
64 if((stream = freopen(name, "w+b", stdout)) != NULL &&
65 system(run) == 0 &&
66 #ifndef __FreeBSD__
67 errno == 0 &&
68 #endif
69 fseek(stream, 0, SEEK_SET) == 0 &&
70 fgets(currev, sizeof currev, stream) == currev &&
71 (isdigit(currev[0]) || (currev[0] == '-' && currev[1] == '1')))
73 { 72 {
74 *hash = '\0'; 73 gotrev = 1;
75 hash++; 74 // [BB] Find the date the revision of the working copy was created.
75 if ( ( svnCheckout == 0 ) &&
76 ( system("hg log -r. --template \"{date|hgdate} {node|short}\"") == 0 ) &&
77 ( fseek(stream, strlen(currev), SEEK_SET) == 0 ) &&
78 ( fgets(hgdateString, sizeof ( hgdateString ), stream) == hgdateString ) )
79 {
80 // [BB] Find the hash in the output and store it.
81 char *p = strrchr ( hgdateString, ' ' );
82 strncpy ( hgHash, p ? ( p+1 ) : "hashnotfound" , sizeof( hgHash ) - 1 );
83 hgHash[ sizeof ( hgHash ) - 1 ] = '\0';
84 // [BB] Extract the date from the output and store it.
85 hgdate = atoi ( hgdateString );
86 }
76 } 87 }
77 } 88 }
78 if (hash == NULL) 89 if (stream != NULL)
79 { 90 {
80 fprintf(stderr, "Failed to get commit info: %s\n", strerror(errno)); 91 fclose (stream);
81 strcpy(vertag, "<unknown version>"); 92 remove (name);
82 lastlog[0] = '\0'; 93 }
83 lastlog[1] = '0'; 94 if (name != NULL)
84 lastlog[2] = '\0'; 95 {
85 hash = lastlog + 1; 96 free (name);
86 } 97 }
87 98
88 stream = fopen (argv[1], "r"); 99 if (!gotrev)
100 {
101 fprintf (stderr, "Failed to get current revision: %s\n", strerror(errno));
102 strcpy (currev, "0");
103 rev = currev;
104 }
105 else
106 {
107 rev = strchr (currev, ':');
108 if (rev == NULL)
109 {
110 rev = currev;
111 }
112 else
113 {
114 rev += 1;
115 }
116 }
117
118 // [BB] Create date version string.
119 if ( gotrev && ( svnCheckout == 0 ) )
120 {
121 char *endptr;
122 unsigned long parsedRev = strtoul(rev, &endptr, 10);
123 unsigned int localChanges = ( *endptr == '+' );
124 struct tm *lt = gmtime( &hgdate );
125 if ( localChanges )
126 sprintf ( rev, "%d%02d%02d-%02d%02dM", lt->tm_year - 100, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min);
127 else
128 sprintf ( rev, "%d%02d%02d-%02d%02d", lt->tm_year - 100, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min);
129 }
130
131 stream = fopen (argv[2], "r");
89 if (stream != NULL) 132 if (stream != NULL)
90 { 133 {
91 if (!gotrev) 134 if (!gotrev)
92 { // If we didn't get a revision but the file does exist, leave it alone. 135 { // If we didn't get a revision but the file does exist, leave it alone.
136 fprintf( stderr, "No revision found.\n" );
93 fclose (stream); 137 fclose (stream);
94 return 0; 138 return 0;
95 } 139 }
96
97 // Read the revision that's in this file already. If it's the same as 140 // Read the revision that's in this file already. If it's the same as
98 // what we've got, then we don't need to modify it and can avoid rebuilding 141 // what we've got, then we don't need to modify it and can avoid rebuilding
99 // dependant files. 142 // dependant files.
100 if (fgets(lasthash, sizeof lasthash, stream) == lasthash) 143 if (fgets(lastrev, sizeof lastrev, stream) == lastrev)
101 { 144 {
102 stripnl(lasthash); 145 if (lastrev[0] != '\0')
103 if (strcmp(hash, lasthash + 3) == 0) 146 { // Strip trailing \n
147 lastrev[strlen(lastrev) - 1] = '\0';
148 }
149 if (strcmp(rev, lastrev + 3) == 0)
104 { 150 {
105 needupdate = 0; 151 needupdate = 0;
106 } 152 }
107 } 153 }
108 fclose (stream); 154 fclose (stream);
109 } 155 }
110 156
111 if (needupdate) 157 if (needupdate)
112 { 158 {
113 stream = fopen (argv[1], "w"); 159 stream = fopen (argv[2], "w");
114 if (stream == NULL) 160 if (stream == NULL)
115 { 161 {
116 return 1; 162 return 1;
117 } 163 }
118 fprintf(stream, 164 // [BB] Use hgdate as revision number.
165 if ( hgdate )
166 urev = hgdate;
167 else
168 urev = strtoul(rev, NULL, 10);
169 fprintf (stream,
119 "// %s\n" 170 "// %s\n"
120 "//\n" 171 "//\n"
121 "// This file was automatically generated by the\n" 172 "// This file was automatically generated by the\n"
122 "// updaterevision tool. Do not edit by hand.\n" 173 "// updaterevision tool. Do not edit by hand.\n"
123 "\n" 174 "\n"
124 "#define GIT_DESCRIPTION \"%s\"\n" 175 "#define SVN_REVISION_STRING \"%s\"\n"
125 "#define GIT_HASH \"%s\"\n" 176 "#define SVN_REVISION_NUMBER %lu\n",
126 "#define GIT_TIME \"%s\"\n", 177 rev, rev, urev);
127 hash, vertag, hash, lastlog); 178
128 fclose(stream); 179 // [BB] Also save the hg hash.
129 fprintf(stderr, "%s updated to commit %s.\n", argv[1], vertag); 180 if ( svnCheckout == 0 )
181 fprintf (stream, "#define HG_REVISION_HASH_STRING \"%s\"\n", hgHash);
182
183 fclose (stream);
184 fprintf (stderr, "%s updated to revision %s.\n", argv[2], rev);
130 } 185 }
131 else 186 else
132 { 187 {
133 fprintf (stderr, "%s is up to date at commit %s.\n", argv[1], vertag); 188 fprintf (stderr, "%s is up to date at revision %s.\n", argv[2], rev);
134 } 189 }
135 190
136 return 0; 191 return 0;
137 } 192 }

mercurial