]> granicus.if.org Git - python/commitdiff
Adds support for an unattend.xml file to control the Windows installer options.
authorSteve Dower <steve.dower@microsoft.com>
Sat, 18 Jul 2015 16:28:19 +0000 (09:28 -0700)
committerSteve Dower <steve.dower@microsoft.com>
Sat, 18 Jul 2015 16:28:19 +0000 (09:28 -0700)
Doc/using/windows.rst
Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp

index 53d5f3e3518b22446abfeed8309c10d6da5913ca..dad827dadc9c7c08a1b332b74fe567167b82f229 100644 (file)
@@ -171,6 +171,20 @@ display a simplified initial page and disallow customization::
 recommended for per-user installs when there is also a system-wide installation
 that included the launcher.)
 
+The options listed above can also be provided in a file named ``unattend.xml``
+alongside the executable. This file specifies a list of options and values.
+When a value is provided as an attribute, it will be converted to a number if
+possible. Values provided as element text are always left as strings. This
+example file sets the same options and the previous example::
+
+    <Options>
+        <Option Name="InstallAllUsers" Value="no" />
+        <Option Name="Include_launcher" Value="0" />
+        <Option Name="Include_test" Value="no" />
+        <Option Name="SimpleInstall" Value="yes" />
+        <Option Name="SimpleInstallDescription">Just for me, no test suite</Option>
+    </Options>
+
 .. _install-layout-option:
 
 Installing Without Downloading
index 2d8f04d9d59c5d89eb8f4740c31d2aeaae134f23..416f2d5e1999368566d89115d74a98d1470c965c 100644 (file)
@@ -1249,6 +1249,92 @@ private:
         return hr;
     }
 
+    //
+    // ParseVariablesFromUnattendXml - reads options from unattend.xml if it
+    // exists
+    //
+    HRESULT ParseVariablesFromUnattendXml() {
+        HRESULT hr = S_OK;
+        LPWSTR sczUnattendXmlPath = nullptr;
+        IXMLDOMDocument *pixdUnattend = nullptr;
+        IXMLDOMNodeList *pNodes = nullptr;
+        IXMLDOMNode *pNode = nullptr;
+        long cNodes;
+        DWORD dwAttr;
+        LPWSTR scz = nullptr;
+        BOOL bValue;
+        int iValue;
+        BOOL tryConvert;
+        BSTR bstrValue = nullptr;
+
+        hr = BalFormatString(L"[WixBundleOriginalSourceFolder]unattend.xml", &sczUnattendXmlPath);
+        BalExitOnFailure(hr, "Failed to calculate path to unattend.xml");
+
+        if (!FileExistsEx(sczUnattendXmlPath, &dwAttr)) {
+            BalLog(BOOTSTRAPPER_LOG_LEVEL_VERBOSE, "Did not find %ls", sczUnattendXmlPath);
+            hr = S_FALSE;
+            goto LExit;
+        }
+
+        hr = XmlLoadDocumentFromFile(sczUnattendXmlPath, &pixdUnattend);
+        BalExitOnFailure1(hr, "Failed to read %ls", sczUnattendXmlPath);
+
+        // get the list of variables users have overridden
+        hr = XmlSelectNodes(pixdUnattend, L"/Options/Option", &pNodes);
+        if (S_FALSE == hr) {
+            ExitFunction1(hr = S_OK);
+        }
+        BalExitOnFailure(hr, "Failed to select option nodes.");
+
+        hr = pNodes->get_length((long*)&cNodes);
+        BalExitOnFailure(hr, "Failed to get option node count.");
+
+        BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Reading settings from %ls", sczUnattendXmlPath);
+
+        for (DWORD i = 0; i < cNodes; ++i) {
+            hr = XmlNextElement(pNodes, &pNode, nullptr);
+            BalExitOnFailure(hr, "Failed to get next node.");
+
+            // @Name
+            hr = XmlGetAttributeEx(pNode, L"Name", &scz);
+            BalExitOnFailure(hr, "Failed to get @Name.");
+
+            tryConvert = TRUE;
+            hr = XmlGetAttribute(pNode, L"Value", &bstrValue);
+            if (FAILED(hr) || !bstrValue || !*bstrValue) {
+                hr = XmlGetText(pNode, &bstrValue);
+                tryConvert = FALSE;
+            }
+            BalExitOnFailure(hr, "Failed to get @Value.");
+
+            if (tryConvert &&
+                CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"yes", -1)) {
+                _engine->SetVariableNumeric(scz, 1);
+            } else if (tryConvert &&
+                       CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"no", -1)) {
+                _engine->SetVariableNumeric(scz, 0);
+            } else if (tryConvert && ::StrToIntExW(bstrValue, STIF_DEFAULT, &iValue)) {
+                _engine->SetVariableNumeric(scz, iValue);
+            } else {
+                _engine->SetVariableString(scz, bstrValue);
+            }
+
+            ReleaseNullBSTR(bstrValue);
+            ReleaseNullStr(scz);
+            ReleaseNullObject(pNode);
+        }
+
+        BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Finished reading from %ls", sczUnattendXmlPath);
+
+    LExit:
+        ReleaseObject(pNode);
+        ReleaseObject(pNodes);
+        ReleaseObject(pixdUnattend);
+        ReleaseStr(sczUnattendXmlPath);
+
+        return hr;
+    }
+
 
     //
     // InitializeData - initializes all the package information.
@@ -1264,6 +1350,9 @@ private:
         hr = ParseOverridableVariablesFromXml(pixdManifest);
         BalExitOnFailure(hr, "Failed to read overridable variables.");
 
+        hr = ParseVariablesFromUnattendXml();
+        ExitOnFailure(hr, "Failed to read unattend.ini file.");
+
         hr = ProcessCommandLine(&_language);
         ExitOnFailure(hr, "Unknown commandline parameters.");
 
@@ -1323,7 +1412,9 @@ private:
 
                         hr = StrAllocString(psczLanguage, &argv[i][0], 0);
                         BalExitOnFailure(hr, "Failed to copy language.");
-                    } 
+                    } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"simple", -1)) {
+                        _engine->SetVariableNumeric(L"SimpleInstall", 1);
+                    }
                 } else if (_overridableVariables) {
                     int value;
                     const wchar_t* pwc = wcschr(argv[i], L'=');