Skip to content

Commit ce1a316

Browse files
committed
add version and build info functions
1 parent 6a93cfa commit ce1a316

File tree

4 files changed

+235
-0
lines changed

4 files changed

+235
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* Copyright (c) 2018, Oracle and/or its affiliates.
2+
* Copyright (C) 1996-2017 Python Software Foundation
3+
*
4+
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
5+
*/
6+
7+
#include "Python.h"
8+
9+
#ifndef DONT_HAVE_STDIO_H
10+
#include <stdio.h>
11+
#endif
12+
13+
#ifndef DATE
14+
#ifdef __DATE__
15+
#define DATE __DATE__
16+
#else
17+
#define DATE "xx/xx/xx"
18+
#endif
19+
#endif
20+
21+
#ifndef TIME
22+
#ifdef __TIME__
23+
#define TIME __TIME__
24+
#else
25+
#define TIME "xx:xx:xx"
26+
#endif
27+
#endif
28+
29+
/* XXX Only unix build process has been tested */
30+
#ifndef GITVERSION
31+
#define GITVERSION ""
32+
#endif
33+
#ifndef GITTAG
34+
#define GITTAG ""
35+
#endif
36+
#ifndef GITBRANCH
37+
#define GITBRANCH ""
38+
#endif
39+
40+
const char *
41+
Py_GetBuildInfo(void)
42+
{
43+
static char buildinfo[50 + sizeof(GITVERSION) +
44+
((sizeof(GITTAG) > sizeof(GITBRANCH)) ?
45+
sizeof(GITTAG) : sizeof(GITBRANCH))];
46+
const char *revision = _Py_gitversion();
47+
const char *sep = *revision ? ":" : "";
48+
const char *gitid = _Py_gitidentifier();
49+
if (!(*gitid))
50+
gitid = "default";
51+
PyOS_snprintf(buildinfo, sizeof(buildinfo),
52+
"%s%s%s, %.20s, %.9s", gitid, sep, revision,
53+
DATE, TIME);
54+
return buildinfo;
55+
}
56+
57+
const char *
58+
_Py_gitversion(void)
59+
{
60+
return GITVERSION;
61+
}
62+
63+
const char *
64+
_Py_gitidentifier(void)
65+
{
66+
const char *gittag, *gitid;
67+
gittag = GITTAG;
68+
if ((*gittag) && strcmp(gittag, "undefined") != 0)
69+
gitid = gittag;
70+
else
71+
gitid = GITBRANCH;
72+
return gitid;
73+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* Copyright (c) 2018, Oracle and/or its affiliates.
2+
* Copyright (C) 1996-2017 Python Software Foundation
3+
*
4+
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
5+
*/
6+
7+
/* Return the compiler identification, if possible. */
8+
9+
#include "Python.h"
10+
11+
#ifndef COMPILER
12+
13+
// Note the __clang__ conditional has to come before the __GNUC__ one because
14+
// clang pretends to be GCC.
15+
#if defined(__clang__)
16+
#define COMPILER "\n[Clang " __clang_version__ "]"
17+
#elif defined(__GNUC__)
18+
#define COMPILER "\n[GCC " __VERSION__ "]"
19+
// Generic fallbacks.
20+
#elif defined(__cplusplus)
21+
#define COMPILER "[C++]"
22+
#else
23+
#define COMPILER "[C]"
24+
#endif
25+
26+
#endif /* !COMPILER */
27+
28+
const char *
29+
Py_GetCompiler(void)
30+
{
31+
return COMPILER;
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* Copyright (c) 2018, Oracle and/or its affiliates.
2+
* Copyright (C) 1996-2017 Python Software Foundation
3+
*
4+
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
5+
*/
6+
7+
/* Return the full version string. */
8+
9+
#include "Python.h"
10+
11+
#include "patchlevel.h"
12+
13+
const char *
14+
Py_GetVersion(void)
15+
{
16+
static char version[250];
17+
PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s",
18+
PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler());
19+
return version;
20+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/* Copyright (c) 2018, Oracle and/or its affiliates.
2+
* Copyright (C) 1996-2017 Python Software Foundation
3+
*
4+
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
5+
*/
6+
7+
#include "Python.h"
8+
9+
/* snprintf() wrappers. If the platform has vsnprintf, we use it, else we
10+
emulate it in a half-hearted way. Even if the platform has it, we wrap
11+
it because platforms differ in what vsnprintf does in case the buffer
12+
is too small: C99 behavior is to return the number of characters that
13+
would have been written had the buffer not been too small, and to set
14+
the last byte of the buffer to \0. At least MS _vsnprintf returns a
15+
negative value instead, and fills the entire buffer with non-\0 data.
16+
17+
The wrappers ensure that str[size-1] is always \0 upon return.
18+
19+
PyOS_snprintf and PyOS_vsnprintf never write more than size bytes
20+
(including the trailing '\0') into str.
21+
22+
If the platform doesn't have vsnprintf, and the buffer size needed to
23+
avoid truncation exceeds size by more than 512, Python aborts with a
24+
Py_FatalError.
25+
26+
Return value (rv):
27+
28+
When 0 <= rv < size, the output conversion was unexceptional, and
29+
rv characters were written to str (excluding a trailing \0 byte at
30+
str[rv]).
31+
32+
When rv >= size, output conversion was truncated, and a buffer of
33+
size rv+1 would have been needed to avoid truncation. str[size-1]
34+
is \0 in this case.
35+
36+
When rv < 0, "something bad happened". str[size-1] is \0 in this
37+
case too, but the rest of str is unreliable. It could be that
38+
an error in format codes was detected by libc, or on platforms
39+
with a non-C99 vsnprintf simply that the buffer wasn't big enough
40+
to avoid truncation, or on platforms without any vsnprintf that
41+
PyMem_Malloc couldn't obtain space for a temp buffer.
42+
43+
CAUTION: Unlike C99, str != NULL and size > 0 are required.
44+
*/
45+
46+
int
47+
PyOS_snprintf(char *str, size_t size, const char *format, ...)
48+
{
49+
int rc;
50+
va_list va;
51+
52+
va_start(va, format);
53+
rc = PyOS_vsnprintf(str, size, format, va);
54+
va_end(va);
55+
return rc;
56+
}
57+
58+
int
59+
PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
60+
{
61+
int len; /* # bytes written, excluding \0 */
62+
#ifdef HAVE_SNPRINTF
63+
#define _PyOS_vsnprintf_EXTRA_SPACE 1
64+
#else
65+
#define _PyOS_vsnprintf_EXTRA_SPACE 512
66+
char *buffer;
67+
#endif
68+
assert(str != NULL);
69+
assert(size > 0);
70+
assert(format != NULL);
71+
/* We take a size_t as input but return an int. Sanity check
72+
* our input so that it won't cause an overflow in the
73+
* vsnprintf return value or the buffer malloc size. */
74+
if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) {
75+
len = -666;
76+
goto Done;
77+
}
78+
79+
#ifdef HAVE_SNPRINTF
80+
len = vsnprintf(str, size, format, va);
81+
#else
82+
/* Emulate it. */
83+
buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE);
84+
if (buffer == NULL) {
85+
len = -666;
86+
goto Done;
87+
}
88+
89+
len = vsprintf(buffer, format, va);
90+
if (len < 0)
91+
/* ignore the error */;
92+
93+
else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE)
94+
Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
95+
96+
else {
97+
const size_t to_copy = (size_t)len < size ?
98+
(size_t)len : size - 1;
99+
assert(to_copy < size);
100+
memcpy(str, buffer, to_copy);
101+
str[to_copy] = '\0';
102+
}
103+
PyMem_FREE(buffer);
104+
#endif
105+
Done:
106+
if (size > 0)
107+
str[size-1] = '\0';
108+
return len;
109+
#undef _PyOS_vsnprintf_EXTRA_SPACE
110+
}

0 commit comments

Comments
 (0)