VirtualBox

Ignore:
Timestamp:
Nov 2, 2020 4:31:41 PM (4 years ago)
Author:
vboxsync
Message:

Windows host installer: Implemented a new warning dialog that shows if the Python bindings dependencies are not met. More code for the Python bindings detection + installation. Only got limited testing so far. bugref:9855

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp

    r86777 r86782  
    144144
    145145/**
    146  * Tries to retrieve the Python installation path on the system.
     146 * Waits for a started process to terminate.
     147 *
     148 * @returns VBox status code.
     149 * @param   Process             Handle of process to wait for.
     150 * @param   msTimeout           Timeout (in ms) to wait for process to terminate.
     151 * @param   pProcSts            Pointer to process status on return.
     152 */
     153static int procWait(RTPROCESS Process, RTMSINTERVAL msTimeout, PRTPROCSTATUS pProcSts)
     154{
     155    uint64_t tsStartMs = RTTimeMilliTS();
     156
     157    while (RTTimeMilliTS() - tsStartMs <= msTimeout)
     158    {
     159        int rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, pProcSts);
     160        if (rc == VERR_PROCESS_RUNNING)
     161            continue;
     162        else if (RT_FAILURE(rc))
     163            return rc;
     164
     165        if (   pProcSts->iStatus   != 0
     166            || pProcSts->enmReason != RTPROCEXITREASON_NORMAL)
     167        {
     168            rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
     169        }
     170
     171        return VINF_SUCCESS;
     172    }
     173
     174    return VERR_TIMEOUT;
     175}
     176
     177/**
     178 * Runs an executable on the OS.
     179 *
     180 * @returns VBox status code.
     181 * @param   hModule             Windows installer module handle.
     182 * @param   pcsExe              Absolute path of executable to run.
     183 * @param   papcszArgs          Pointer to command line arguments to use for calling the executable.
     184 * @param   cArgs               Number of command line arguments in \a papcszArgs.
     185 */
     186static int procRun(MSIHANDLE hModule, const char *pcszImage, const char **papcszArgs, size_t cArgs)
     187{
     188    RT_NOREF(cArgs);
     189
     190    RTPROCESS Process;
     191    RT_ZERO(Process);
     192
     193    uint32_t fProcess  = 0;
     194#ifndef DEBUG
     195             fProcess |= RTPROC_FLAGS_HIDDEN;
     196#endif
     197
     198    int rc = RTProcCreate(pcszImage, papcszArgs, RTENV_DEFAULT, fProcess, &Process);
     199    if (RT_SUCCESS(rc))
     200    {
     201        RTPROCSTATUS ProcSts;
     202        RT_ZERO(ProcSts);
     203
     204        rc = procWait(Process, RT_MS_30SEC, &ProcSts);
     205
     206        if (RT_FAILURE(rc))
     207            logStringF(hModule, "procRun: Waiting for process \"%s\" failed with %Rrc (process status: %d, reason: %d)\n",
     208                       pcszImage, rc, ProcSts.iStatus, ProcSts.enmReason);
     209    }
     210    else
     211        logStringF(hModule, "procRun: Creating process for \"%s\" failed with %Rrc\n", pcszImage, rc);
     212
     213    return rc;
     214}
     215
     216/**
     217 * Tries to retrieve the Python installation path on the system, extended version.
    147218 *
    148219 * @returns VBox status code.
     
    152223 *                              Must be free'd by the caller using RTStrFree().
    153224 */
    154 static int getPythonPath(MSIHANDLE hModule, HKEY hKeyRoot, char **ppszPath)
     225static int getPythonPathEx(MSIHANDLE hModule, HKEY hKeyRoot, char **ppszPath)
    155226{
    156227    HKEY hkPythonCore = NULL;
     
    194265        dwErr = RegQueryValueExW(hkPythonInstPath, L"", NULL, &dwKeyType, (LPBYTE)wszVal, &dwKey);
    195266        if (dwErr == ERROR_SUCCESS)
    196             logStringF(hModule, "InstallPythonAPI: Path \"%ls\" found.", wszVal);
     267            logStringF(hModule, "getPythonPath: Path \"%ls\" found.", wszVal);
    197268
    198269        if (pszPythonPath) /* Free former path, if any. */
     
    207278        if (!RTPathExists(pszPythonPath))
    208279        {
    209             logStringF(hModule, "InstallPythonAPI: Warning: Path \"%s\" does not exist, skipping.", wszVal);
     280            logStringF(hModule, "getPythonPath: Warning: Defined path \"%s\" does not exist, skipping.", wszVal);
    210281            rc = VERR_PATH_NOT_FOUND;
    211282        }
     
    227298
    228299/**
    229  * Waits for a started process to terminate.
     300 * Retrieves the absolute path of the Python installation.
    230301 *
    231302 * @returns VBox status code.
    232  * @param   Process             Handle of process to wait for.
    233  * @param   msTimeout           Timeout (in ms) to wait for process to terminate.
    234  * @param   pProcSts            Pointer to process status on return.
     303 * @param   hModule             Windows installer module handle.
     304 * @param   ppszPath            Where to store the absolute path of the Python installation.
     305 *                              Must be free'd by the caller.
    235306 */
    236 int procWait(RTPROCESS Process, RTMSINTERVAL msTimeout, PRTPROCSTATUS pProcSts)
    237 {
    238     uint64_t tsStartMs = RTTimeMilliTS();
    239 
    240     while (RTTimeMilliTS() - tsStartMs <= msTimeout)
    241     {
    242         int rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, pProcSts);
    243         if (rc == VERR_PROCESS_RUNNING)
    244             continue;
    245         else if (RT_FAILURE(rc))
    246             return rc;
    247 
    248         if (   pProcSts->iStatus   != 0
    249             || pProcSts->enmReason != RTPROCEXITREASON_NORMAL)
    250         {
    251             rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
    252         }
    253 
    254         return VINF_SUCCESS;
    255     }
    256 
    257     return VERR_TIMEOUT;
     307static int getPythonPath(MSIHANDLE hModule, char **ppszPath)
     308{
     309    int rc = getPythonPathEx(hModule, HKEY_LOCAL_MACHINE, ppszPath);
     310    if (RT_FAILURE(rc))
     311        rc = getPythonPathEx(hModule, HKEY_CURRENT_USER, ppszPath);
     312
     313    return rc;
     314}
     315
     316/**
     317 * Retrieves the absolute path of the Python executable.
     318 *
     319 * @returns VBox status code.
     320 * @param   hModule             Windows installer module handle.
     321 * @param   ppszPythonExe       Where to store the absolute path of the Python executable.
     322 *                              Must be free'd by the caller.
     323 */
     324static int getPythonExe(MSIHANDLE hModule, char **ppszPythonExe)
     325{
     326    int rc = getPythonPath(hModule, ppszPythonExe);
     327    if (RT_SUCCESS(rc))
     328        rc = RTStrAAppend(ppszPythonExe, "python.exe"); /** @todo Can this change? */
     329
     330    return rc;
     331}
     332
     333/**
     334 * Checks if all dependencies for running the VBox Python API bindings are met.
     335 *
     336 * @returns VBox status code, or error if depedencies are not met.
     337 * @param   hModule             Windows installer module handle.
     338 * @param   pcszPythonExe       Path to Python interpreter image (.exe).
     339 */
     340static int checkPythonDependencies(MSIHANDLE hModule, const char *pcszPythonExe)
     341{
     342    /*
     343     * Check if importing the win32api module works.
     344     * This is a prerequisite for setting up the VBox API.
     345     */
     346    logStringF(hModule, "checkPythonDependencies: Checking for win32api extensions ...");
     347
     348    const char *papszArgs[4] = { pcszPythonExe, "-c", "import win32api", NULL};
     349
     350    int rc = procRun(hModule, pcszPythonExe, papszArgs, RT_ELEMENTS(papszArgs));
     351    if (RT_SUCCESS(rc))
     352    {
     353        logStringF(hModule, "checkPythonDependencies: win32api found\n");
     354    }
     355    else
     356        logStringF(hModule, "checkPythonDependencies: Importing win32api failed with %Rrc\n", rc);
     357
     358    return rc;
    258359}
    259360
     
    272373{
    273374    char *pszPythonPath;
    274     int rc = getPythonPath(hModule, HKEY_LOCAL_MACHINE, &pszPythonPath);
    275     if (RT_FAILURE(rc))
    276         rc = getPythonPath(hModule, HKEY_CURRENT_USER, &pszPythonPath);
    277 
     375    int rc = getPythonPath(hModule, &pszPythonPath);
    278376    if (RT_SUCCESS(rc))
    279377    {
     
    296394        logStringF(hModule, "IsPythonInstalled: Error: No suitable Python installation found (%Rrc), skipping installation.", rc);
    297395
     396    if (RT_FAILURE(rc))
     397        logStringF(hModule, "IsPythonInstalled: Python seems not to be installed (%Rrc); please download + install the Python Core package.", rc);
     398
    298399    VBoxSetMsiProp(hModule, L"VBOX_PYTHON_INSTALLED", RT_SUCCESS(rc) ? L"1" : L"0");
     400
     401    return ERROR_SUCCESS; /* Never return failure. */
     402}
     403
     404/**
     405 * Checks if all dependencies for running the VBox Python API bindings are met.
     406 *
     407 * Called from the MSI installer as custom action.
     408 *
     409 * @returns Always ERROR_SUCCESS.
     410 *          Sets public property VBOX_PYTHON_DEPS_INSTALLED to "0" (false) or "1" (success).
     411 *
     412 * @param   hModule             Windows installer module handle.
     413 */
     414UINT __stdcall ArePythonAPIDepsInstalled(MSIHANDLE hModule)
     415{
     416    char *pszPythonExe;
     417    int rc = getPythonExe(hModule, &pszPythonExe);
     418    if (RT_SUCCESS(rc))
     419    {
     420        rc = checkPythonDependencies(hModule, pszPythonExe);
     421        if (RT_SUCCESS(rc))
     422            logStringF(hModule, "ArePythonAPIDepsInstalled: Dependencies look good.\n");
     423
     424        RTStrFree(pszPythonExe);
     425    }
     426
     427    if (RT_FAILURE(rc))
     428        logStringF(hModule, "ArePythonAPIDepsInstalled: Failed with %Rrc\n", rc);
     429
     430    VBoxSetMsiProp(hModule, L"VBOX_PYTHON_DEPS_INSTALLED", RT_SUCCESS(rc) ? L"1" : L"0");
    299431
    300432    return ERROR_SUCCESS; /* Never return failure. */
     
    315447    logStringF(hModule, "InstallPythonAPI: Checking for installed Python environment(s) ...");
    316448
    317     char *pszPythonPath;
    318     int rc = getPythonPath(hModule, HKEY_LOCAL_MACHINE, &pszPythonPath);
     449    char *pszPythonExe;
     450    int rc = getPythonExe(hModule, &pszPythonExe);
    319451    if (RT_FAILURE(rc))
    320         rc = getPythonPath(hModule, HKEY_CURRENT_USER, &pszPythonPath);
    321 
    322     if (RT_FAILURE(rc))
    323452    {
    324453        VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"0");
    325 
    326         logStringF(hModule, "InstallPythonAPI: Python seems not to be installed (%Rrc); please download + install the Python Core package.", rc);
    327454        return ERROR_SUCCESS;
    328     }
    329 
    330     logStringF(hModule, "InstallPythonAPI: Python installation found at \"%s\".", pszPythonPath);
    331     logStringF(hModule, "InstallPythonAPI: Checking for win32api extensions ...");
    332 
    333     uint32_t fProcess = 0;
    334 #ifndef DEBUG
    335              fProcess |= RTPROC_FLAGS_HIDDEN;
    336 #endif
    337 
    338     char szPythonPath[RTPATH_MAX] = { 0 };
    339     rc = RTPathAppend(szPythonPath, sizeof(szPythonPath), pszPythonPath);
    340     if (RT_SUCCESS(rc))
    341     {
    342         rc = RTPathAppend(szPythonPath, sizeof(szPythonPath), "python.exe");
    343         if (RT_SUCCESS(rc))
    344         {
    345             /*
    346              * Check if importing the win32api module works.
    347              * This is a prerequisite for setting up the VBox API.
    348              */
    349             RTPROCESS Process;
    350             RT_ZERO(Process);
    351             const char *papszArgs[4] = { szPythonPath, "-c", "import win32api", NULL};
    352             rc = RTProcCreate(szPythonPath, papszArgs, RTENV_DEFAULT, fProcess, &Process);
    353             if (RT_SUCCESS(rc))
    354             {
    355                 RTPROCSTATUS ProcSts;
    356                 rc = procWait(Process, RT_MS_30SEC, &ProcSts);
    357                 if (RT_FAILURE(rc))
    358                     logStringF(hModule, "InstallPythonAPI: Importing the win32api failed with %d (exit reason: %d)\n",
    359                                    ProcSts.iStatus, ProcSts.enmReason);
    360             }
    361         }
    362455    }
    363456
     
    365458     * Set up the VBox API.
    366459     */
     460    /* Get the VBox API setup string. */
     461    char *pszVBoxSDKPath;
     462    rc = VBoxGetMsiPropUtf8(hModule, "CustomActionData", &pszVBoxSDKPath);
    367463    if (RT_SUCCESS(rc))
    368464    {
    369         /* Get the VBox API setup string. */
    370         char *pszVBoxSDKPath;
    371         rc = VBoxGetMsiPropUtf8(hModule, "CustomActionData", &pszVBoxSDKPath);
     465        /* Make sure our current working directory is the VBox installation path. */
     466        rc = RTPathSetCurrent(pszVBoxSDKPath);
    372467        if (RT_SUCCESS(rc))
    373468        {
    374             /* Make sure our current working directory is the VBox installation path. */
    375             rc = RTPathSetCurrent(pszVBoxSDKPath);
     469            /* Set required environment variables. */
     470            rc = RTEnvSet("VBOX_INSTALL_PATH", pszVBoxSDKPath);
    376471            if (RT_SUCCESS(rc))
    377472            {
    378                 /* Set required environment variables. */
    379                 rc = RTEnvSet("VBOX_INSTALL_PATH", pszVBoxSDKPath);
     473                logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%s\" ...\n", pszVBoxSDKPath);
     474
     475                const char *papszArgs[4] = { pszPythonExe, "vboxapisetup.py", "install", NULL};
     476
     477                rc = procRun(hModule, pszPythonExe, papszArgs, RT_ELEMENTS(papszArgs));
    380478                if (RT_SUCCESS(rc))
    381                 {
    382                     RTPROCSTATUS ProcSts;
    383                     RT_ZERO(ProcSts);
    384 
    385                     logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%s\" ...\n", pszVBoxSDKPath);
    386 
    387                     RTPROCESS Process;
    388                     RT_ZERO(Process);
    389                     const char *papszArgs[4] = { szPythonPath, "vboxapisetup.py", "install", NULL};
    390                     rc = RTProcCreate(szPythonPath, papszArgs, RTENV_DEFAULT, fProcess, &Process);
    391                     if (RT_SUCCESS(rc))
    392                     {
    393                         rc = procWait(Process, RT_MS_30SEC, &ProcSts);
    394                         if (RT_SUCCESS(rc))
    395                             logStringF(hModule, "InstallPythonAPI: Installation of vboxapisetup.py successful\n");
    396                     }
    397 
    398                     if (RT_FAILURE(rc))
    399                         logStringF(hModule, "InstallPythonAPI: Calling vboxapisetup.py failed with %Rrc (process status: %d, exit reason: %d)\n",
    400                                    rc, ProcSts.iStatus, ProcSts.enmReason);
    401                 }
    402                 else
    403                     logStringF(hModule, "InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH, rc=%Rrc\n", rc);
     479                    logStringF(hModule, "InstallPythonAPI: Installation of vboxapisetup.py successful\n");
     480
     481                if (RT_FAILURE(rc))
     482                    logStringF(hModule, "InstallPythonAPI: Calling vboxapisetup.py failed with %Rrc\n", rc);
    404483            }
    405484            else
    406                 logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%s\", rc=%Rrc\n", pszVBoxSDKPath, rc);
    407 
    408             RTStrFree(pszVBoxSDKPath);
     485                logStringF(hModule, "InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH, rc=%Rrc\n", rc);
    409486        }
    410487        else
    411             logStringF(hModule, "InstallPythonAPI: Unable to retrieve VBox installation directory, rc=%Rrc\n", rc);
    412     }
     488            logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%s\", rc=%Rrc\n", pszVBoxSDKPath, rc);
     489
     490        RTStrFree(pszVBoxSDKPath);
     491    }
     492    else
     493        logStringF(hModule, "InstallPythonAPI: Unable to retrieve VBox installation directory, rc=%Rrc\n", rc);
    413494
    414495    /*
     
    419500        logStringF(hModule, "InstallPythonAPI: Validating VBox API ...\n");
    420501
    421         RTPROCSTATUS ProcSts;
    422         RT_ZERO(ProcSts);
    423 
    424         RTPROCESS Process;
    425         RT_ZERO(Process);
    426         const char *papszArgs[4] = { szPythonPath, "-c", "from vboxapi import VirtualBoxManager", NULL};
    427         rc = RTProcCreate(szPythonPath, papszArgs, RTENV_DEFAULT, fProcess, &Process);
     502        const char *papszArgs[4] = { pszPythonExe, "-c", "from vboxapi import VirtualBoxManager", NULL};
     503
     504        rc = procRun(hModule, pszPythonExe, papszArgs, RT_ELEMENTS(papszArgs));
    428505        if (RT_SUCCESS(rc))
    429         {
    430             rc = procWait(Process, RT_MS_30SEC, &ProcSts);
    431             if (RT_SUCCESS(rc))
    432                 logStringF(hModule, "InstallPythonAPI: VBox API looks good.\n");
    433         }
     506            logStringF(hModule, "InstallPythonAPI: VBox API looks good.\n");
    434507
    435508        if (RT_FAILURE(rc))
    436             logStringF(hModule, "InstallPythonAPI: Validating VBox API failed with %Rrc (process status: %d, exit reason: %d)\n",
    437                        rc, ProcSts.iStatus, ProcSts.enmReason);
    438     }
    439 
    440     RTStrFree(pszPythonPath);
     509            logStringF(hModule, "InstallPythonAPI: Validating VBox API failed with %Rrc\n", rc);
     510    }
     511
     512    RTStrFree(pszPythonExe);
    441513
    442514    VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", RT_SUCCESS(rc) ? L"1" : L"0");
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette