5 # Copyright The SCons Foundation
7 # Permission is hereby granted, free of charge, to any person obtaining
8 # a copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sublicense, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
15 # The above copyright notice and this permission notice shall be included
16 # in all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 This script is intended to be called by ninja to start up the scons daemon process. It will
28 launch the server and attempt to connect to it. This process needs to completely detach
29 from the spawned process so ninja can consider the build edge completed. It should be passed
30 the args which should be forwarded to the scons daemon process which could be any number of
31 # arguments. However the first few arguments are required to be port, ninja dir, and keep alive
34 The scons_daemon_dirty file acts as a pidfile marker letting this script quickly skip over
35 restarting the server if the server is running. The assumption here is the pidfile should only
36 exist if the server is running.
51 ninja_builddir
= pathlib
.Path(sys
.argv
[2])
52 daemon_dir
= pathlib
.Path(tempfile
.gettempdir()) / (
53 "scons_daemon_" + str(hashlib
.md5(str(ninja_builddir
).encode()).hexdigest())
55 os
.makedirs(daemon_dir
, exist_ok
=True)
58 filename
=daemon_dir
/ "scons_daemon.log",
60 format
="%(asctime)s %(message)s",
64 def log_error(msg
) -> None:
68 if not os
.path
.exists(ninja_builddir
/ "scons_daemon_dirty"):
71 str(pathlib
.Path(__file__
).parent
/ "ninja_scons_daemon.py"),
73 logging
.debug(f
"Starting daemon with {' '.join(cmd)}")
76 # TODO: Remove the following when Python3.6 support is dropped.
77 if sys
.platform
== 'win32' and sys
.version_info
[0] == 3 and sys
.version_info
[1] == 6:
78 # on Windows with Python version 3.6, popen does not do a good job disconnecting
79 # the std handles and this make ninja hang because they stay open to the original
80 # process ninja launched. Here we can force the handles to be separated.
81 # See: https://docs.python.org/3.6/library/subprocess.html#subprocess.STARTUPINFO
82 # See Also: https://docs.python.org/3.6/library/subprocess.html#subprocess.Popen
83 # Note when you don't specify stdin, stdout, and/or stderr they default to None
84 # which indicates no output redirection will occur.
85 si
= subprocess
.STARTUPINFO()
86 si
.dwFlags
= subprocess
.STARTF_USESTDHANDLES
88 cmd
, close_fds
=True, shell
=False, startupinfo
=si
92 cmd
, stdout
=subprocess
.DEVNULL
, stderr
=subprocess
.DEVNULL
, shell
=False,
94 with
open(daemon_dir
/ "pidfile", "w") as f
:
96 with
open(ninja_builddir
/ "scons_daemon_dirty", "w") as f
:
99 error_msg
= f
"ERROR: Failed to connect to scons daemon.\n Check {daemon_dir / 'scons_daemon.log'} for more info.\n"
103 logging
.debug("Attempting to connect scons daemon")
104 conn
= http
.client
.HTTPConnection(
105 "127.0.0.1", port
=int(sys
.argv
[1]), timeout
=60
107 conn
.request("GET", "/?ready=true")
111 response
= conn
.getresponse()
112 except (http
.client
.RemoteDisconnected
, http
.client
.ResponseNotReady
, socket
.timeout
):
114 except http
.client
.HTTPException
:
115 log_error(f
"Error: {traceback.format_exc()}")
118 msg
= response
.read()
119 status
= response
.status
121 log_error(msg
.decode("utf-8"))
123 logging
.debug("Server Responded it was ready!")
126 except ConnectionRefusedError
:
127 logging
.debug(f
"Server not ready, server PID: {p.pid}")
129 if p
.poll() is not None:
130 log_error(f
"Server process died, aborting: {p.returncode}")
131 sys
.exit(p
.returncode
)
132 except ConnectionResetError
:
133 log_error("Server ConnectionResetError")
136 log_error(f
"Error: {traceback.format_exc()}")
141 # indent-tabs-mode:nil
143 # vim: set expandtab tabstop=4 shiftwidth=4: