@@ -6,7 +6,7 @@ use crate::{
66 helpers:: {
77 app_paths:: { app_dir, tauri_dir} ,
88 command_env,
9- config:: { get as get_config, reload as reload_config} ,
9+ config:: { get as get_config, reload as reload_config, AppUrl , WindowUrl } ,
1010 manifest:: { get_workspace_members, rewrite_manifest} ,
1111 Logger ,
1212 } ,
@@ -22,15 +22,15 @@ use shared_child::SharedChild;
2222use std:: {
2323 env:: set_current_dir,
2424 ffi:: OsStr ,
25- process:: { exit, Child , Command } ,
25+ process:: { exit, Command } ,
2626 sync:: {
2727 mpsc:: { channel, Receiver } ,
2828 Arc , Mutex ,
2929 } ,
3030 time:: Duration ,
3131} ;
3232
33- static BEFORE_DEV : OnceCell < Mutex < Child > > = OnceCell :: new ( ) ;
33+ static BEFORE_DEV : OnceCell < Mutex < Arc < SharedChild > > > = OnceCell :: new ( ) ;
3434
3535#[ derive( Debug , Parser ) ]
3636#[ clap( about = "Tauri dev" ) ]
@@ -107,22 +107,42 @@ pub fn command(options: Options) -> Result<()> {
107107 if !before_dev. is_empty ( ) {
108108 logger. log ( format ! ( "Running `{}`" , before_dev) ) ;
109109 #[ cfg( target_os = "windows" ) ]
110- let child = Command :: new ( "cmd" )
111- . arg ( "/S" )
112- . arg ( "/C" )
113- . arg ( before_dev)
114- . current_dir ( app_dir ( ) )
115- . envs ( command_env ( true ) ) // development build always includes debug information
116- . spawn ( )
117- . with_context ( || format ! ( "failed to run `{}` with `cmd /C`" , before_dev) ) ?;
110+ let mut command = {
111+ let mut command = Command :: new ( "cmd" ) ;
112+ command
113+ . arg ( "/S" )
114+ . arg ( "/C" )
115+ . arg ( before_dev)
116+ . current_dir ( app_dir ( ) )
117+ . envs ( command_env ( true ) ) ; // development build always includes debug information
118+ command
119+ } ;
118120 #[ cfg( not( target_os = "windows" ) ) ]
119- let child = Command :: new ( "sh" )
120- . arg ( "-c" )
121- . arg ( before_dev)
122- . current_dir ( app_dir ( ) )
123- . envs ( command_env ( true ) ) // development build always includes debug information
124- . spawn ( )
125- . with_context ( || format ! ( "failed to run `{}` with `sh -c`" , before_dev) ) ?;
121+ let mut command = {
122+ let mut command = Command :: new ( "sh" ) ;
123+ command
124+ . arg ( "-c" )
125+ . arg ( before_dev)
126+ . current_dir ( app_dir ( ) )
127+ . envs ( command_env ( true ) ) ; // development build always includes debug information
128+ command
129+ } ;
130+
131+ let child = SharedChild :: spawn ( & mut command)
132+ . unwrap_or_else ( |_| panic ! ( "failed to run `{}`" , before_dev) ) ;
133+ let child = Arc :: new ( child) ;
134+ let child_ = child. clone ( ) ;
135+ let logger_ = logger. clone ( ) ;
136+ std:: thread:: spawn ( move || {
137+ let status = child_
138+ . wait ( )
139+ . expect ( "failed to wait on \" beforeDevCommand\" " ) ;
140+ if !status. success ( ) {
141+ logger_. error ( "The \" beforeDevCommand\" terminated with a non-zero status code." ) ;
142+ exit ( status. code ( ) . unwrap_or ( 1 ) ) ;
143+ }
144+ } ) ;
145+
126146 BEFORE_DEV . set ( Mutex :: new ( child) ) . unwrap ( ) ;
127147 }
128148 }
@@ -169,6 +189,62 @@ pub fn command(options: Options) -> Result<()> {
169189 let ( child_wait_tx, child_wait_rx) = channel ( ) ;
170190 let child_wait_rx = Arc :: new ( Mutex :: new ( child_wait_rx) ) ;
171191
192+ if std:: env:: var_os ( "TAURI_SKIP_DEVSERVER_CHECK" ) != Some ( "true" . into ( ) ) {
193+ if let AppUrl :: Url ( WindowUrl :: External ( dev_server_url) ) = config
194+ . lock ( )
195+ . unwrap ( )
196+ . as_ref ( )
197+ . unwrap ( )
198+ . build
199+ . dev_path
200+ . clone ( )
201+ {
202+ let host = dev_server_url
203+ . host ( )
204+ . unwrap_or_else ( || panic ! ( "No host name in the URL" ) ) ;
205+ let port = dev_server_url
206+ . port_or_known_default ( )
207+ . unwrap_or_else ( || panic ! ( "No port number in the URL" ) ) ;
208+ let addrs;
209+ let addr;
210+ let addrs = match host {
211+ url:: Host :: Domain ( domain) => {
212+ use std:: net:: ToSocketAddrs ;
213+ addrs = ( domain, port) . to_socket_addrs ( ) ?;
214+ addrs. as_slice ( )
215+ }
216+ url:: Host :: Ipv4 ( ip) => {
217+ addr = ( ip, port) . into ( ) ;
218+ std:: slice:: from_ref ( & addr)
219+ }
220+ url:: Host :: Ipv6 ( ip) => {
221+ addr = ( ip, port) . into ( ) ;
222+ std:: slice:: from_ref ( & addr)
223+ }
224+ } ;
225+ let mut i = 0 ;
226+ let sleep_interval = std:: time:: Duration :: from_secs ( 2 ) ;
227+ let max_attempts = 90 ;
228+ loop {
229+ if std:: net:: TcpStream :: connect ( addrs) . is_ok ( ) {
230+ break ;
231+ }
232+ if i % 3 == 0 {
233+ logger. warn ( "Waiting for your dev server to start..." ) ;
234+ }
235+ i += 1 ;
236+ if i == max_attempts {
237+ logger. error ( format ! (
238+ "Could not connect to `{}` after {}s. Please make sure that is the URL to your dev server." ,
239+ dev_server_url, i * sleep_interval. as_secs( )
240+ ) ) ;
241+ exit ( 1 ) ;
242+ }
243+ std:: thread:: sleep ( sleep_interval) ;
244+ }
245+ }
246+ }
247+
172248 let mut process = start_app ( & options, & runner, & cargo_features, child_wait_rx. clone ( ) ) ;
173249
174250 let ( tx, rx) = channel ( ) ;
@@ -221,7 +297,7 @@ pub fn command(options: Options) -> Result<()> {
221297
222298fn kill_before_dev_process ( ) {
223299 if let Some ( child) = BEFORE_DEV . get ( ) {
224- let mut child = child. lock ( ) . unwrap ( ) ;
300+ let child = child. lock ( ) . unwrap ( ) ;
225301 #[ cfg( windows) ]
226302 let _ = Command :: new ( "powershell" )
227303 . arg ( "-NoProfile" )
0 commit comments