| 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 } |