summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Heller <theller@ctypes.org>2004-02-20 14:43:21 (GMT)
committerThomas Heller <theller@ctypes.org>2004-02-20 14:43:21 (GMT)
commita19cdad6dc2815f6044c56601e8dd81d9c219631 (patch)
tree4abd679481bc6d2a26c69ac98ad6b9404fa8c171
parentbb990588988365d6af01aba41ae88e76dcdc7933 (diff)
downloadcpython-a19cdad6dc2815f6044c56601e8dd81d9c219631.zip
cpython-a19cdad6dc2815f6044c56601e8dd81d9c219631.tar.gz
cpython-a19cdad6dc2815f6044c56601e8dd81d9c219631.tar.bz2
Patch #892660 from Mark Hammond, for distutils bdist_wininst command.
install.c: support for a 'pre-install-script', run before anything has been installed. Provides a 'message_box' module function for use by either the pre-install or post-install scripts. bdist_wininst.py: support for pre-install script. Typo (build->built), fixes so that --target-version can still work, even when the distribution has extension modules - in this case, we insist on --skip-build, as we still can't actually build other versions.
-rw-r--r--Lib/distutils/command/bdist_wininst.py32
-rw-r--r--PC/bdist_wininst/install.c227
2 files changed, 206 insertions, 53 deletions
diff --git a/Lib/distutils/command/bdist_wininst.py b/Lib/distutils/command/bdist_wininst.py
index 3c4c893..76b1762 100644
--- a/Lib/distutils/command/bdist_wininst.py
+++ b/Lib/distutils/command/bdist_wininst.py
@@ -43,6 +43,10 @@ class bdist_wininst (Command):
('install-script=', None,
"basename of installation script to be run after"
"installation or before deinstallation"),
+ ('pre-install-script=', None,
+ "Fully qualified filename of a script to be run before "
+ "any files are installed. This script need not be in the "
+ "distribution"),
]
boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
@@ -59,6 +63,7 @@ class bdist_wininst (Command):
self.title = None
self.skip_build = 0
self.install_script = None
+ self.pre_install_script = None
# initialize_options()
@@ -69,11 +74,12 @@ class bdist_wininst (Command):
self.bdist_dir = os.path.join(bdist_base, 'wininst')
if not self.target_version:
self.target_version = ""
- if self.distribution.has_ext_modules():
+ if not self.skip_build and self.distribution.has_ext_modules():
short_version = get_python_version()
if self.target_version and self.target_version != short_version:
raise DistutilsOptionError, \
- "target version can only be" + short_version
+ "target version can only be %s, or the '--skip_build'" \
+ " option must be specified" % (short_version,)
self.target_version = short_version
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
@@ -109,6 +115,21 @@ class bdist_wininst (Command):
# we do not want to include pyc or pyo files
install_lib.compile = 0
install_lib.optimize = 0
+
+ # If we are building an installer for a Python version other
+ # than the one we are currently running, then we need to ensure
+ # our build_lib reflects the other Python version rather than ours.
+ # Note that for target_version!=sys.version, we must have skipped the
+ # build step, so there is no issue with enforcing the build of this
+ # version.
+ target_version = self.target_version
+ if not target_version:
+ assert self.skip_build, "Should have already checked this"
+ target_version = sys.version[0:3]
+ plat_specifier = ".%s-%s" % (get_platform(), target_version)
+ build = self.get_finalized_command('build')
+ build.build_lib = os.path.join(build.build_base,
+ 'lib' + plat_specifier)
# Use a custom scheme for the zip-file, because we have to decide
# at installation time which scheme to use.
@@ -187,7 +208,7 @@ class bdist_wininst (Command):
lines.append("title=%s" % repr(title)[1:-1])
import time
import distutils
- build_info = "Build %s with distutils-%s" % \
+ build_info = "Built %s with distutils-%s" % \
(time.ctime(time.time()), distutils.__version__)
lines.append("build_info=%s" % build_info)
return string.join(lines, "\n")
@@ -223,6 +244,11 @@ class bdist_wininst (Command):
if bitmap:
file.write(bitmapdata)
+ # Append the pre-install script
+ cfgdata = cfgdata + "\0"
+ if self.pre_install_script:
+ script_data = open(self.pre_install_script, "r").read()
+ cfgdata = cfgdata + script_data + "\n\0"
file.write(cfgdata)
header = struct.pack("<iii",
0x1234567A, # tag
diff --git a/PC/bdist_wininst/install.c b/PC/bdist_wininst/install.c
index 597bd9e..0ea1fcd 100644
--- a/PC/bdist_wininst/install.c
+++ b/PC/bdist_wininst/install.c
@@ -117,6 +117,7 @@ char build_info[80]; /* [Setup] build_info=, distutils version
char meta_name[80]; /* package name without version like
'Distutils' */
char install_script[MAX_PATH];
+char *pre_install_script; /* run before we install a single file */
int py_major, py_minor; /* Python version selected for installation */
@@ -129,6 +130,7 @@ char pythondll[MAX_PATH];
BOOL pyc_compile, pyo_compile;
BOOL success; /* Installation successfull? */
+char *failure_reason = NULL;
HANDLE hBitmap;
char *bitmap_bytes;
@@ -206,6 +208,20 @@ static struct tagFile {
struct tagFile *next;
} *file_list = NULL;
+static void set_failure_reason(char *reason)
+{
+ if (failure_reason)
+ free(failure_reason);
+ failure_reason = strdup(reason);
+ success = FALSE;
+}
+static char *get_failure_reason()
+{
+ if (!failure_reason)
+ return "Installation failed.";
+ return failure_reason;
+}
+
static void add_to_filelist(char *path)
{
struct tagFile *p;
@@ -532,7 +548,7 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args)
hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE);
if (FAILED(hr)) {
g_PyErr_Format(g_PyExc_OSError,
- "Save() failed, error 0x%x", hr);
+ "Failed to create shortcut '%s' - error 0x%x", filename, hr);
goto error;
}
@@ -553,6 +569,17 @@ static PyObject *CreateShortcut(PyObject *self, PyObject *args)
return NULL;
}
+static PyObject *PyMessageBox(PyObject *self, PyObject *args)
+{
+ int rc;
+ char *text, *caption;
+ int flags;
+ if (!g_PyArg_ParseTuple(args, "ssi", &text, &caption, &flags))
+ return NULL;
+ rc = MessageBox(GetFocus(), text, caption, flags);
+ return g_Py_BuildValue("i", rc);
+}
+
#define METH_VARARGS 0x0001
PyMethodDef meth[] = {
@@ -560,8 +587,42 @@ PyMethodDef meth[] = {
{"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL},
{"file_created", FileCreated, METH_VARARGS, NULL},
{"directory_created", DirectoryCreated, METH_VARARGS, NULL},
+ {"message_box", PyMessageBox, METH_VARARGS, NULL},
};
+static int prepare_script_environment(HINSTANCE hPython)
+{
+ PyObject *mod;
+ DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *));
+ DECLPROC(hPython, int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
+ DECLPROC(hPython, PyObject *, PyObject_GetAttrString, (PyObject *, char *));
+ DECLPROC(hPython, PyObject *, PyCFunction_New, (PyMethodDef *, PyObject *));
+ DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
+ DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
+ DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
+ if (!PyImport_ImportModule || !PyObject_GetAttrString ||
+ !PyObject_SetAttrString || !PyCFunction_New)
+ return 1;
+ if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
+ return 1;
+
+ mod = PyImport_ImportModule("__builtin__");
+ if (mod) {
+ int i;
+ g_PyExc_ValueError = PyObject_GetAttrString(mod, "ValueError");
+ g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError");
+ for (i = 0; i < DIM(meth); ++i) {
+ PyObject_SetAttrString(mod, meth[i].ml_name,
+ PyCFunction_New(&meth[i], NULL));
+ }
+ }
+ g_Py_BuildValue = Py_BuildValue;
+ g_PyArg_ParseTuple = PyArg_ParseTuple;
+ g_PyErr_Format = PyErr_Format;
+
+ return 0;
+}
+
/*
* This function returns one of the following error codes:
* 1 if the Python-dll does not export the functions we need
@@ -579,19 +640,12 @@ run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
DECLPROC(hPython, int, PySys_SetArgv, (int, char **));
DECLPROC(hPython, int, PyRun_SimpleFile, (FILE *, char *));
DECLPROC(hPython, void, Py_Finalize, (void));
- DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *));
- DECLPROC(hPython, int, PyObject_SetAttrString,
- (PyObject *, char *, PyObject *));
- DECLPROC(hPython, PyObject *, PyObject_GetAttrString,
- (PyObject *, char *));
DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
DECLPROC(hPython, PyObject *, PyCFunction_New,
(PyMethodDef *, PyObject *));
DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
- PyObject *mod;
-
int result = 0;
FILE *fp;
@@ -599,20 +653,12 @@ run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
|| !PyRun_SimpleFile || !Py_Finalize)
return 1;
- if (!PyImport_ImportModule || !PyObject_SetAttrString
- || !Py_BuildValue)
+ if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
return 1;
if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format)
return 1;
- if (!PyObject_GetAttrString)
- return 1;
-
- g_Py_BuildValue = Py_BuildValue;
- g_PyArg_ParseTuple = PyArg_ParseTuple;
- g_PyErr_Format = PyErr_Format;
-
if (pathname == NULL || pathname[0] == '\0')
return 2;
@@ -627,19 +673,7 @@ run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
Py_Initialize();
- mod = PyImport_ImportModule("__builtin__");
- if (mod) {
- int i;
-
- g_PyExc_ValueError = PyObject_GetAttrString(mod,
- "ValueError");
- g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError");
- for (i = 0; i < DIM(meth); ++i) {
- PyObject_SetAttrString(mod, meth[i].ml_name,
- PyCFunction_New(&meth[i], NULL));
- }
- }
-
+ prepare_script_environment(hPython);
PySys_SetArgv(argc, argv);
result = PyRun_SimpleFile(fp, pathname);
Py_Finalize();
@@ -649,6 +683,77 @@ run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
return result;
}
+static int do_run_simple_script(HINSTANCE hPython, char *script)
+{
+ int rc;
+ DECLPROC(hPython, void, Py_Initialize, (void));
+ DECLPROC(hPython, void, Py_SetProgramName, (char *));
+ DECLPROC(hPython, void, Py_Finalize, (void));
+ DECLPROC(hPython, int, PyRun_SimpleString, (char *));
+ DECLPROC(hPython, void, PyErr_Print, (void));
+
+ if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
+ !PyRun_SimpleString || !PyErr_Print)
+ return -1;
+
+ Py_SetProgramName(modulename);
+ Py_Initialize();
+ prepare_script_environment(hPython);
+ rc = PyRun_SimpleString(script);
+ if (rc)
+ PyErr_Print();
+ Py_Finalize();
+ return rc;
+}
+
+static int run_simple_script(char *script)
+{
+ int rc;
+ char *tempname;
+ HINSTANCE hPython;
+ tempname = tmpnam(NULL);
+ freopen(tempname, "a", stderr);
+ freopen(tempname, "a", stdout);
+
+ hPython = LoadLibrary (pythondll);
+ if (!hPython) {
+ set_failure_reason("Can't load Python for pre-install script");
+ return -1;
+ }
+ rc = do_run_simple_script(hPython, script);
+ FreeLibrary(hPython);
+ fflush(stderr);
+ fflush(stdout);
+ /* We only care about the output when we fail. If the script works
+ OK, then we discard it
+ */
+ if (rc) {
+ int err_buf_size;
+ char *err_buf;
+ const char *prefix = "Running the pre-installation script failed\r\n";
+ int prefix_len = strlen(prefix);
+ FILE *fp = fopen(tempname, "rb");
+ fseek(fp, 0, SEEK_END);
+ err_buf_size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ err_buf = malloc(prefix_len + err_buf_size + 1);
+ if (err_buf) {
+ int n;
+ strcpy(err_buf, prefix);
+ n = fread(err_buf+prefix_len, 1, err_buf_size, fp);
+ err_buf[prefix_len+n] = '\0';
+ fclose(fp);
+ set_failure_reason(err_buf);
+ free(err_buf);
+ } else {
+ set_failure_reason("Out of memory!");
+ }
+ }
+ remove(tempname);
+ return rc;
+}
+
+
static BOOL SystemError(int error, char *msg)
{
char Buffer[1024];
@@ -811,7 +916,11 @@ static void create_bitmap(HWND hwnd)
ReleaseDC(hwnd, hdc);
}
-static char *ExtractIniFile(char *data, DWORD size, int *pexe_size)
+/* Extract everything we need to begin the installation. Currently this
+ is the INI filename with install data, and the raw pre-install script
+*/
+static BOOL ExtractInstallData(char *data, DWORD size, int *pexe_size,
+ char **out_ini_file, char **out_preinstall_script)
{
/* read the end of central directory record */
struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
@@ -828,12 +937,15 @@ static char *ExtractIniFile(char *data, DWORD size, int *pexe_size)
char *ini_file;
char tempdir[MAX_PATH];
+ /* ensure that if we fail, we don't have garbage out pointers */
+ *out_ini_file = *out_preinstall_script = NULL;
+
if (pe->tag != 0x06054b50) {
- return NULL;
+ return FALSE;
}
if (pmd->tag != 0x1234567A || ofs < 0) {
- return NULL;
+ return FALSE;
}
if (pmd->bitmap_size) {
@@ -846,21 +958,26 @@ static char *ExtractIniFile(char *data, DWORD size, int *pexe_size)
src = ((char *)pmd) - pmd->uncomp_size;
ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */
if (!ini_file)
- return NULL;
+ return FALSE;
if (!GetTempPath(sizeof(tempdir), tempdir)
|| !GetTempFileName(tempdir, "~du", 0, ini_file)) {
SystemError(GetLastError(),
"Could not create temporary file");
- return NULL;
+ return FALSE;
}
dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
0, 0, NULL/*notify*/);
if (!dst)
- return NULL;
- memcpy(dst, src, pmd->uncomp_size);
+ return FALSE;
+ /* Up to the first \0 is the INI file data. */
+ strncpy(dst, src, pmd->uncomp_size);
+ src += strlen(dst) + 1;
+ /* Up to next \0 is the pre-install script */
+ *out_preinstall_script = strdup(src);
+ *out_ini_file = ini_file;
UnmapViewOfFile(dst);
- return ini_file;
+ return TRUE;
}
static void PumpMessages(void)
@@ -1354,7 +1471,7 @@ SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
&py_major, &py_minor);
if (result == 2)
#ifdef _DEBUG
- wsprintf(pythondll, "c:\\python22\\PCBuild\\python%d%d_d.dll",
+ wsprintf(pythondll, "python%d%d_d.dll",
py_major, py_minor);
#else
wsprintf(pythondll, "python%d%d.dll",
@@ -1542,6 +1659,7 @@ InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
"Click Cancel to exit the wizard.",
meta_name);
SetDlgItemText(hwnd, IDC_TITLE, Buffer);
+ SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
break;
case WM_NUMFILES:
@@ -1571,6 +1689,7 @@ InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
case PSN_WIZNEXT:
/* Handle a Next button click here */
hDialog = hwnd;
+ success = TRUE;
/* Make sure the installation directory name ends in a */
/* backslash */
@@ -1602,14 +1721,23 @@ InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
}
*/
scheme = GetScheme(py_major, py_minor);
-
+ /* Run the pre-install script. */
+ if (pre_install_script && *pre_install_script) {
+ SetDlgItemText (hwnd, IDC_TITLE,
+ "Running pre-installation script");
+ run_simple_script(pre_install_script);
+ }
+ if (!success) {
+ break;
+ }
/* Extract all files from the archive */
SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
- success = unzip_archive(scheme,
- python_dir, arc_data,
- arc_size, notify);
+ if (!unzip_archive (scheme,
+ python_dir, arc_data,
+ arc_size, notify))
+ set_failure_reason("Failed to unzip installation files");
/* Compile the py-files */
- if (pyc_compile) {
+ if (success && pyc_compile) {
int errors;
HINSTANCE hPython;
SetDlgItemText(hwnd, IDC_TITLE,
@@ -1628,7 +1756,7 @@ InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
* confuse the user.
*/
}
- if (pyo_compile) {
+ if (success && pyo_compile) {
int errors;
HINSTANCE hPython;
SetDlgItemText(hwnd, IDC_TITLE,
@@ -1668,7 +1796,7 @@ FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
IMAGE_BITMAP, (LPARAM)hBitmap);
if (!success)
- SetDlgItemText(hwnd, IDC_INFO, "Installation failed.");
+ SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
/* async delay: will show the dialog box completely before
the install_script is started */
@@ -1677,7 +1805,7 @@ FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_USER:
- if (install_script && install_script[0]) {
+ if (success && install_script && install_script[0]) {
char fname[MAX_PATH];
char *tempname;
FILE *fp;
@@ -2271,9 +2399,8 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
*/
/* Try to extract the configuration data into a temporary file */
- ini_file = ExtractIniFile(arc_data, arc_size, &exe_size);
-
- if (ini_file)
+ if (ExtractInstallData(arc_data, arc_size, &exe_size,
+ &ini_file, &pre_install_script))
return DoInstall();
if (!ini_file && __argc > 1) {