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