From 24156a73ea5e47c78bf1427312013585ab346c4d Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 5 Jul 2023 17:05:11 +1000 Subject: [PATCH 1/2] tugger-wix: Add option to add start menu shortcut. --- tugger-wix/src/simple_msi_builder.rs | 99 +++++++++++++++++++ .../tugger_starlark_type_wix_msi_builder.rst | 9 ++ tugger/src/starlark/wix_msi_builder.rs | 3 + 3 files changed, 111 insertions(+) diff --git a/tugger-wix/src/simple_msi_builder.rs b/tugger-wix/src/simple_msi_builder.rs index 124246515..de0f90322 100644 --- a/tugger-wix/src/simple_msi_builder.rs +++ b/tugger-wix/src/simple_msi_builder.rs @@ -45,6 +45,7 @@ pub struct WiXSimpleMsiBuilder { /// Files to materialize in `Program Files`. program_files_manifest: FileManifest, + add_to_start_menu: Option, upgrade_code: Option, package_keywords: Option, package_description: Option, @@ -117,6 +118,15 @@ impl WiXSimpleMsiBuilder { Ok(()) } + /// Set the ` Self { + self.add_to_start_menu = Some(value); + self + } + /// Set the ` String { + Uuid::new_v5( + &Uuid::NAMESPACE_DNS, + format!("tugger.application_shortcut.{}", self.product_name).as_bytes(), + ) + .as_hyphenated() + .encode_upper(&mut Uuid::encode_buffer()) + .to_string() + } } #[cfg(test)] diff --git a/tugger/docs/tugger_starlark_type_wix_msi_builder.rst b/tugger/docs/tugger_starlark_type_wix_msi_builder.rst index 4ecc7cee2..05e4ece45 100644 --- a/tugger/docs/tugger_starlark_type_wix_msi_builder.rst +++ b/tugger/docs/tugger_starlark_type_wix_msi_builder.rst @@ -94,6 +94,15 @@ If not set, the default is ``-.msi``. + .. py:attribute:: add_to_start_menu + + (``str``) + + The filename (relative to install directory) to add as a shortcut in the Start Menu, + eg. ``msi.add_to_start_menu = ".exe"`` + + If not set no shortcut will be installed. + .. py:attribute:: package_description (``str``) diff --git a/tugger/src/starlark/wix_msi_builder.rs b/tugger/src/starlark/wix_msi_builder.rs index c5f90f7b1..080dea92c 100644 --- a/tugger/src/starlark/wix_msi_builder.rs +++ b/tugger/src/starlark/wix_msi_builder.rs @@ -109,6 +109,9 @@ impl TypedValue for WiXMsiBuilderValue { "upgrade_code" => { inner.builder = inner.builder.clone().upgrade_code(value.to_string()); } + "add_to_start_menu" => { + inner.builder = inner.builder.clone().add_to_start_menu(value.to_string()); + } attr => { return Err(ValueError::OperationNotSupported { op: UnsupportedOperation::SetAttr(attr.to_string()), From 435935ff658247d5c3b73dc03ce041b09df0296f Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 18 Jul 2023 21:05:27 +1000 Subject: [PATCH 2/2] tugger-wix: Add option to add start menu shortcut. --- pyoxidizer/src/py_packaging/binary.rs | 3 + .../src/py_packaging/standalone_builder.rs | 6 ++ pyoxidizer/src/starlark/python_executable.rs | 30 ++++++++++ tugger-wix/src/simple_msi_builder.rs | 56 +++++++++++-------- .../tugger_starlark_type_wix_msi_builder.rst | 13 ++++- tugger/src/starlark/wix_msi_builder.rs | 15 ++++- 6 files changed, 96 insertions(+), 27 deletions(-) diff --git a/pyoxidizer/src/py_packaging/binary.rs b/pyoxidizer/src/py_packaging/binary.rs index fdf2a8513..448409248 100644 --- a/pyoxidizer/src/py_packaging/binary.rs +++ b/pyoxidizer/src/py_packaging/binary.rs @@ -168,6 +168,9 @@ pub trait PythonBinaryBuilder { /// The name of the binary. fn name(&self) -> String; + /// Set the name of the binary. + fn set_name(&mut self, value: &str) -> Result<()>; + /// How the binary will link against libpython. fn libpython_link_mode(&self) -> LibpythonLinkMode; diff --git a/pyoxidizer/src/py_packaging/standalone_builder.rs b/pyoxidizer/src/py_packaging/standalone_builder.rs index 4b937494c..7c4baed05 100644 --- a/pyoxidizer/src/py_packaging/standalone_builder.rs +++ b/pyoxidizer/src/py_packaging/standalone_builder.rs @@ -422,6 +422,12 @@ impl PythonBinaryBuilder for StandalonePythonExecutableBuilder { self.exe_name.clone() } + fn set_name(&mut self, value: &str) -> Result<()> { + self.exe_name = value.to_string(); + + Ok(()) + } + fn libpython_link_mode(&self) -> LibpythonLinkMode { self.link_mode } diff --git a/pyoxidizer/src/starlark/python_executable.rs b/pyoxidizer/src/starlark/python_executable.rs index b68bbdf35..c3f77d136 100644 --- a/pyoxidizer/src/starlark/python_executable.rs +++ b/pyoxidizer/src/starlark/python_executable.rs @@ -188,6 +188,7 @@ impl TypedValue for PythonExecutableValue { Ok(Value::from(exe.windows_runtime_dlls_mode().to_string())) } "windows_subsystem" => Ok(Value::from(exe.windows_subsystem())), + "name" => Ok(Value::from(exe.name())), _ => Err(ValueError::OperationNotSupported { op: UnsupportedOperation::GetAttr(attribute.to_string()), left: Self::TYPE.to_string(), @@ -204,6 +205,7 @@ impl TypedValue for PythonExecutableValue { | "tcl_files_path" | "windows_runtime_dlls_mode" | "windows_subsystem" + | "name" )) } @@ -260,6 +262,11 @@ impl TypedValue for PythonExecutableValue { Ok(()) } + "name" => { + let _ = exe.set_name(value.to_string().as_str()); + + Ok(()) + } _ => Err(ValueError::OperationNotSupported { op: UnsupportedOperation::SetAttr(attribute.to_string()), left: Self::TYPE.to_string(), @@ -962,6 +969,8 @@ impl PythonExecutableValue { builder.add_program_files_manifest(type_values, call_stack, manifest.deref().clone())?; + let _ = builder.set_exe_name(self.get_attr("name").unwrap().to_str())?; + Ok(builder_value.clone()) } @@ -1385,6 +1394,27 @@ mod tests { Ok(()) } + #[test] + fn name() -> Result<()> { + let mut env = test_evaluation_context_builder()?.into_context()?; + add_exe(&mut env)?; + + let v = env.eval("exe.name")?; + assert_eq!(v.get_type(), "string"); + assert_eq!(v.to_string(), "COPYING.txt"); + + env.eval("exe.name = 'myapp'")?; + let v = env.eval("exe.name")?; + assert_eq!(v.get_type(), "string"); + assert_eq!(v.to_string(), "myapp"); + + env.eval("exe.name = None")?; + let v = env.eval("exe.name")?; + assert_eq!(v.get_type(), "NoneType"); + + Ok(()) + } + #[test] fn test_windows_runtime_dlls_mode() -> Result<()> { let mut env = test_evaluation_context_builder()?.into_context()?; diff --git a/tugger-wix/src/simple_msi_builder.rs b/tugger-wix/src/simple_msi_builder.rs index de0f90322..96d1b9d3f 100644 --- a/tugger-wix/src/simple_msi_builder.rs +++ b/tugger-wix/src/simple_msi_builder.rs @@ -45,7 +45,8 @@ pub struct WiXSimpleMsiBuilder { /// Files to materialize in `Program Files`. program_files_manifest: FileManifest, - add_to_start_menu: Option, + add_to_start_menu: Option, + exe_name: Option, upgrade_code: Option, package_keywords: Option, package_description: Option, @@ -92,6 +93,15 @@ impl WiXSimpleMsiBuilder { Ok(()) } + /// Set the `exe_name` attribute value. + /// + /// This should be set to the name of the exe file to add to the start menu shortcut. + pub fn set_exe_name(&mut self, value: String) -> Result<()> { + self.exe_name = Some(value); + + Ok(()) + } + /// Attempt to add the Visual C++ Redistributable DLLs to the program files manifest. /// /// This will use `vswhere.exe` to attempt to locate a Visual Studio installation @@ -119,10 +129,8 @@ impl WiXSimpleMsiBuilder { } /// Set the ` Self { + pub fn add_to_start_menu(mut self, value: bool) -> Self { self.add_to_start_menu = Some(value); self } @@ -420,7 +428,7 @@ impl WiXSimpleMsiBuilder { writer.write(XmlEvent::end_element().name("Directory"))?; writer.write(XmlEvent::end_element().name("Directory"))?; - if self.add_to_start_menu.is_some() { + if self.add_to_start_menu.unwrap_or(false) { writer.write(XmlEvent::start_element("Directory").attr("Id", "ProgramMenuFolder"))?; writer.write(XmlEvent::end_element().name("Directory"))?; @@ -428,7 +436,8 @@ impl WiXSimpleMsiBuilder { writer.write(XmlEvent::end_element().name("Directory"))?; - if let Some(shortcut_target) = &self.add_to_start_menu { + if self.add_to_start_menu.unwrap_or(false) { + let shortcut_target = &self.exe_name.as_ref().unwrap_or_else(|| panic!("exe_name not set")); writer.write(XmlEvent::start_element("DirectoryRef").attr("Id", "ProgramMenuFolder"))?; writer.write( XmlEvent::start_element("Component") @@ -436,7 +445,7 @@ impl WiXSimpleMsiBuilder { .attr("Guid", &self.shortcut_guid()) )?; - let full_target = &format!("[APPLICATIONFOLDER]{}", shortcut_target); + let full_target = &format!("[APPLICATIONFOLDER]{}.exe", shortcut_target); let shortcut = XmlEvent::start_element("Shortcut") .attr("Id", "ApplicationStartMenuShortcut") @@ -497,6 +506,23 @@ impl WiXSimpleMsiBuilder { .attr("Absent", "disallow"), )?; + if self.add_to_start_menu.unwrap_or(false) { + writer.write( + XmlEvent::start_element("Feature") + .attr("Id", "ApplicationShortcutFeature") + .attr("Title", "Add Start Menu Shortcut") + .attr( + "Description", + "Adds the application to the Start Menu", + ) + .attr("Level", "1") + .attr("Absent", "allow"), + )?; + writer.write(XmlEvent::start_element("ComponentRef").attr("Id", "ApplicationShortcut"))?; + writer.write(XmlEvent::end_element().name("ComponentRef"))?; + writer.write(XmlEvent::end_element().name("Feature"))?; + } + // Add group for all files derived from self.program_files_manifest. writer.write( XmlEvent::start_element("ComponentGroupRef") @@ -524,22 +550,6 @@ impl WiXSimpleMsiBuilder { writer.write(XmlEvent::end_element().name("ComponentRef"))?; writer.write(XmlEvent::end_element().name("Feature"))?; - if self.add_to_start_menu.is_some() { - writer.write( - XmlEvent::start_element("Feature") - .attr("Id", "ApplicationShortcutFeature") - .attr("Title", "Add Start Menu Shortcut") - .attr( - "Description", - "Adds the application to the Start Menu", - ) - .attr("Level", "1") - .attr("Absent", "allow"), - )?; - writer.write(XmlEvent::start_element("ComponentRef").attr("Id", "ApplicationShortcut"))?; - writer.write(XmlEvent::end_element().name("ComponentRef"))?; - writer.write(XmlEvent::end_element().name("Feature"))?; - } writer.write(XmlEvent::end_element().name("Feature"))?; writer.write( diff --git a/tugger/docs/tugger_starlark_type_wix_msi_builder.rst b/tugger/docs/tugger_starlark_type_wix_msi_builder.rst index 05e4ece45..16fb63e44 100644 --- a/tugger/docs/tugger_starlark_type_wix_msi_builder.rst +++ b/tugger/docs/tugger_starlark_type_wix_msi_builder.rst @@ -96,13 +96,20 @@ .. py:attribute:: add_to_start_menu - (``str``) + (``bool``) - The filename (relative to install directory) to add as a shortcut in the Start Menu, - eg. ``msi.add_to_start_menu = ".exe"`` + If enabled a shortcut wil be created in the Start Menu pointing to ``exe_name`` + eg. ``msi.add_to_start_menu = True`` If not set no shortcut will be installed. + .. py:attribute:: exe_name + + (``str``) + + The filename of the installed application, used by add_to_start_menu. + This is automatically set when using ``exe.to_wix_msi_builder()``. + .. py:attribute:: package_description (``str``) diff --git a/tugger/src/starlark/wix_msi_builder.rs b/tugger/src/starlark/wix_msi_builder.rs index 080dea92c..f23a9b7f0 100644 --- a/tugger/src/starlark/wix_msi_builder.rs +++ b/tugger/src/starlark/wix_msi_builder.rs @@ -110,7 +110,10 @@ impl TypedValue for WiXMsiBuilderValue { inner.builder = inner.builder.clone().upgrade_code(value.to_string()); } "add_to_start_menu" => { - inner.builder = inner.builder.clone().add_to_start_menu(value.to_string()); + inner.builder = inner.builder.clone().add_to_start_menu(value.to_bool()); + } + "set_exe_name" => { + let _ = inner.builder.set_exe_name(value.to_string()); } attr => { return Err(ValueError::OperationNotSupported { @@ -328,6 +331,16 @@ impl WiXMsiBuilderValue { }) } + pub fn set_exe_name(&mut self, exe_name: String) -> ValueResult { + const LABEL: &str = "WiXMSIBuilder.set_exe_name()"; + + let mut inner = self.inner(LABEL)?; + + let _ = inner.builder.set_exe_name(exe_name); + + Ok(Value::new(NoneType::None)) + } + pub fn to_file_content( &self, type_values: &TypeValues,