]> granicus.if.org Git - python/commitdiff
Patch #892660 from Mark Hammond, for distutils bdist_wininst command.
authorThomas Heller <theller@ctypes.org>
Fri, 20 Feb 2004 14:43:21 +0000 (14:43 +0000)
committerThomas Heller <theller@ctypes.org>
Fri, 20 Feb 2004 14:43:21 +0000 (14:43 +0000)
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.

Lib/distutils/command/bdist_wininst.py
PC/bdist_wininst/install.c

index 3c4c893920038aff21a6db01583738a4336b9efd..76b1762edefb63818f31e9b757f5dc06fa851494 100644 (file)
@@ -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
index 597bd9e8eb0919259b45a36abb1956ba6d18611c..0ea1fcdbaff6ae8a442953cf5ecf3172b7f48524 100644 (file)
@@ -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) {