@@ -55,11 +55,18 @@ const NSIS_REQUIRED_FILES: &[&str] = &[
5555 "Include/nsDialogs.nsh" ,
5656 "Include/WinMessages.nsh" ,
5757] ;
58+ const NSIS_PLUGIN_FILES : & [ & str ] = & [
59+ "NSISdl.dll" ,
60+ "StartMenu.dll" ,
61+ "System.dll" ,
62+ "nsDialogs.dll" ,
63+ "additional/nsis_tauri_utils.dll" ,
64+ ] ;
5865#[ cfg( not( target_os = "windows" ) ) ]
59- const NSIS_REQUIRED_FILES : & [ & str ] = & [ "Plugins/x86-unicode/nsis_tauri_utils.dll" ] ;
66+ const NSIS_REQUIRED_FILES : & [ & str ] = & [ "Plugins/x86-unicode/additional/ nsis_tauri_utils.dll" ] ;
6067
6168const NSIS_REQUIRED_FILES_HASH : & [ ( & str , & str , & str , HashAlgorithm ) ] = & [ (
62- "Plugins/x86-unicode/nsis_tauri_utils.dll" ,
69+ "Plugins/x86-unicode/additional/ nsis_tauri_utils.dll" ,
6370 NSIS_TAURI_UTILS_URL ,
6471 NSIS_TAURI_UTILS_SHA1 ,
6572 HashAlgorithm :: Sha1 ,
@@ -96,7 +103,10 @@ pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<P
96103 log:: warn!( "NSIS directory contains mis-hashed files. Redownloading them." ) ;
97104 for ( path, url, hash, hash_algorithm) in mismatched {
98105 let data = download_and_verify ( url, hash, * hash_algorithm) ?;
99- fs:: write ( nsis_toolset_path. join ( path) , data) ?;
106+ let out_path = nsis_toolset_path. join ( path) ;
107+ std:: fs:: create_dir_all ( out_path. parent ( ) . context ( "output path has no parent" ) ?)
108+ . context ( "failed to create file output directory" ) ?;
109+ fs:: write ( out_path, data) . with_context ( || format ! ( "failed to save {path}" ) ) ?;
100110 }
101111 }
102112 }
@@ -116,6 +126,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
116126 fs:: rename ( _tauri_tools_path. join ( "nsis-3.08" ) , nsis_toolset_path) ?;
117127 }
118128
129+ // download additional plugins
119130 let nsis_plugins = nsis_toolset_path. join ( "Plugins" ) ;
120131
121132 let data = download_and_verify (
@@ -124,7 +135,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
124135 HashAlgorithm :: Sha1 ,
125136 ) ?;
126137
127- let target_folder = nsis_plugins. join ( "x86-unicode" ) ;
138+ let target_folder = nsis_plugins. join ( "x86-unicode" ) . join ( "additional" ) ;
128139 fs:: create_dir_all ( & target_folder) ?;
129140 fs:: write ( target_folder. join ( "nsis_tauri_utils.dll" ) , data) ?;
130141
@@ -156,7 +167,7 @@ fn try_add_numeric_build_number(version_str: &str) -> anyhow::Result<String> {
156167
157168fn build_nsis_app_installer (
158169 settings : & Settings ,
159- _nsis_toolset_path : & Path ,
170+ # [ allow ( unused_variables ) ] nsis_toolset_path : & Path ,
160171 tauri_tools_path : & Path ,
161172 updater : bool ,
162173) -> crate :: Result < Vec < PathBuf > > {
@@ -180,19 +191,83 @@ fn build_nsis_app_installer(
180191 }
181192 fs:: create_dir_all ( & output_path) ?;
182193
194+ // we make a copy of the NSIS directory if we're going to sign its DLLs
195+ // because we don't want to change the DLL hashes so the cache can reuse it
196+ let maybe_plugin_copy_path = if settings. can_sign ( ) {
197+ // find nsis path
198+ #[ cfg( target_os = "linux" ) ]
199+ let system_nsis_toolset_path = std:: env:: var_os ( "NSIS_PATH" )
200+ . map ( PathBuf :: from)
201+ . unwrap_or_else ( || PathBuf :: from ( "/usr/share/nsis" ) ) ;
202+ #[ cfg( target_os = "macos" ) ]
203+ let system_nsis_toolset_path = std:: env:: var_os ( "NSIS_PATH" )
204+ . map ( PathBuf :: from)
205+ . ok_or_else ( || anyhow:: anyhow!( "failed to resolve NSIS path" ) )
206+ . or_else ( |_| {
207+ let mut makensis_path =
208+ which:: which ( "makensis" ) . context ( "failed to resolve `makensis`; did you install nsis? See https://tauri.app/distribute/windows-installer/#install-nsis for more information" ) ?;
209+ // homebrew installs it as a symlink
210+ if makensis_path. is_symlink ( ) {
211+ // read_link might return a path relative to makensis_path so we must use join() and canonicalize
212+ makensis_path = makensis_path
213+ . parent ( )
214+ . context ( "missing makensis parent" ) ?
215+ . join ( std:: fs:: read_link ( & makensis_path) . context ( "failed to resolve makensis symlink" ) ?)
216+ . canonicalize ( )
217+ . context ( "failed to resolve makensis path" ) ?;
218+ }
219+ // file structure:
220+ // ├── bin
221+ // │ ├── makensis
222+ // ├── share
223+ // │ ├── nsis
224+ let bin_folder = makensis_path. parent ( ) . context ( "missing makensis parent" ) ?;
225+ let root_folder = bin_folder. parent ( ) . context ( "missing makensis root" ) ?;
226+ crate :: Result :: Ok ( root_folder. join ( "share" ) . join ( "nsis" ) )
227+ } ) ?;
228+ #[ cfg( windows) ]
229+ let system_nsis_toolset_path = nsis_toolset_path. to_path_buf ( ) ;
230+
231+ let plugins_path = output_path. join ( "Plugins" ) ;
232+ // copy system plugins (we don't want to modify system installed DLLs, and on some systems there will even be permission errors if we try)
233+ crate :: utils:: fs_utils:: copy_dir (
234+ & system_nsis_toolset_path. join ( "Plugins" ) . join ( "x86-unicode" ) ,
235+ & plugins_path. join ( "x86-unicode" ) ,
236+ )
237+ . context ( "failed to copy system NSIS Plugins folder to local copy" ) ?;
238+ // copy our downloaded DLLs
239+ crate :: utils:: fs_utils:: copy_dir (
240+ & nsis_toolset_path
241+ . join ( "Plugins" )
242+ . join ( "x86-unicode" )
243+ . join ( "additional" ) ,
244+ & plugins_path. join ( "x86-unicode" ) . join ( "additional" ) ,
245+ )
246+ . context ( "failed to copy additional NSIS Plugins folder to local copy" ) ?;
247+ Some ( plugins_path)
248+ } else {
249+ // in this case plugin_copy_path can be None, we'll use the system default path
250+ None
251+ } ;
252+
183253 let mut data = BTreeMap :: new ( ) ;
184254
185255 let bundle_id = settings. bundle_identifier ( ) ;
186256 let manufacturer = settings
187257 . publisher ( )
188258 . unwrap_or_else ( || bundle_id. split ( '.' ) . nth ( 1 ) . unwrap_or ( bundle_id) ) ;
189259
190- #[ cfg( not( target_os = "windows" ) ) ]
191- {
192- let mut dir = dirs:: cache_dir ( ) . unwrap ( ) ;
193- dir. extend ( [ "tauri" , "NSIS" , "Plugins" , "x86-unicode" ] ) ;
194- data. insert ( "additional_plugins_path" , to_json ( dir) ) ;
195- }
260+ let additional_plugins_path = maybe_plugin_copy_path
261+ . clone ( )
262+ . unwrap_or_else ( || nsis_toolset_path. join ( "Plugins" ) )
263+ . join ( "x86-unicode" )
264+ . join ( "additional" ) ;
265+
266+ data. insert (
267+ "additional_plugins_path" ,
268+ // either our Plugins copy (when signing) or the cache/Plugins/x86-unicode path
269+ to_json ( & additional_plugins_path) ,
270+ ) ;
196271
197272 data. insert ( "arch" , to_json ( arch) ) ;
198273 data. insert ( "bundle_id" , to_json ( bundle_id) ) ;
@@ -526,13 +601,29 @@ fn build_nsis_app_installer(
526601 ) ) ;
527602 fs:: create_dir_all ( nsis_installer_path. parent ( ) . unwrap ( ) ) ?;
528603
529- log:: info!( action = "Running" ; "makensis.exe to produce {}" , display_path( & nsis_installer_path) ) ;
604+ if settings. can_sign ( ) {
605+ log:: info!( "Signing NSIS plugins" ) ;
606+ for dll in NSIS_PLUGIN_FILES {
607+ let path = additional_plugins_path. join ( dll) ;
608+ if path. exists ( ) {
609+ try_sign ( & path, settings) ?;
610+ } else {
611+ log:: warn!( "Could not find {}, skipping signing" , path. display( ) ) ;
612+ }
613+ }
614+ }
615+
616+ log:: info!( action = "Running" ; "makensis to produce {}" , display_path( & nsis_installer_path) ) ;
530617
531618 #[ cfg( target_os = "windows" ) ]
532- let mut nsis_cmd = Command :: new ( _nsis_toolset_path . join ( "makensis.exe" ) ) ;
619+ let mut nsis_cmd = Command :: new ( nsis_toolset_path . join ( "makensis.exe" ) ) ;
533620 #[ cfg( not( target_os = "windows" ) ) ]
534621 let mut nsis_cmd = Command :: new ( "makensis" ) ;
535622
623+ if let Some ( plugins_path) = & maybe_plugin_copy_path {
624+ nsis_cmd. env ( "NSISPLUGINS" , plugins_path) ;
625+ }
626+
536627 nsis_cmd
537628 . args ( [ "-INPUTCHARSET" , "UTF8" , "-OUTPUTCHARSET" , "UTF8" ] )
538629 . arg ( match settings. log_level ( ) {
@@ -628,6 +719,9 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
628719 let loader_path =
629720 dunce:: simplified ( & settings. project_out_directory ( ) . join ( "WebView2Loader.dll" ) ) . to_path_buf ( ) ;
630721 if loader_path. exists ( ) {
722+ if settings. can_sign ( ) {
723+ try_sign ( & loader_path, settings) ?;
724+ }
631725 added_resources. push ( loader_path. clone ( ) ) ;
632726 resources. insert (
633727 loader_path,
@@ -650,6 +744,10 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
650744 }
651745 added_resources. push ( resource_path. clone ( ) ) ;
652746
747+ if settings. can_sign ( ) {
748+ try_sign ( & resource_path, settings) ?;
749+ }
750+
653751 let target_path = resource. target ( ) ;
654752 resources. insert (
655753 resource_path,
0 commit comments