summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@microsoft.com>2015-07-18 16:28:19 (GMT)
committerSteve Dower <steve.dower@microsoft.com>2015-07-18 16:28:19 (GMT)
commit2434aa24e049e54fb6573d7e5126b410c4569037 (patch)
tree15c5a58dbf864f42a59b400dae25afe3cd076493
parenta3d03ec6b1191c04fb4073e26069d3282c0b6e72 (diff)
downloadcpython-2434aa24e049e54fb6573d7e5126b410c4569037.zip
cpython-2434aa24e049e54fb6573d7e5126b410c4569037.tar.gz
cpython-2434aa24e049e54fb6573d7e5126b410c4569037.tar.bz2
Adds support for an unattend.xml file to control the Windows installer options.
-rw-r--r--Doc/using/windows.rst14
-rw-r--r--Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp93
2 files changed, 106 insertions, 1 deletions
diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst
index 53d5f3e..dad827d 100644
--- a/Doc/using/windows.rst
+++ b/Doc/using/windows.rst
@@ -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
diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
index 2d8f04d..416f2d5 100644
--- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
+++ b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
@@ -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'=');