[Python-checkins] cpython (3.5): Adds support for an unattend.xml file to control the Windows installer options.

steve.dower python-checkins at python.org
Sat Jul 18 18:30:09 CEST 2015


https://hg.python.org/cpython/rev/d41944d15256
changeset:   96937:d41944d15256
branch:      3.5
user:        Steve Dower <steve.dower at microsoft.com>
date:        Sat Jul 18 09:28:19 2015 -0700
summary:
  Adds support for an unattend.xml file to control the Windows installer options.

files:
  Doc/using/windows.rst                                        |  14 +
  Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp |  93 +++++++++-
  2 files changed, 106 insertions(+), 1 deletions(-)


diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst
--- a/Doc/using/windows.rst
+++ b/Doc/using/windows.rst
@@ -171,6 +171,20 @@
 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
diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
--- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
+++ b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
@@ -1249,6 +1249,92 @@
         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 @@
         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 @@
 
                         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'=');

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list