52 # be specifying the version(s) of Mercurial they are tested with, or |
46 # be specifying the version(s) of Mercurial they are tested with, or |
53 # leave the attribute unspecified. |
47 # leave the attribute unspecified. |
54 testedwith = 'ships-with-hg-core' |
48 testedwith = 'ships-with-hg-core' |
55 |
49 |
56 def uisetup(ui): |
50 def uisetup(ui): |
57 if pycompat.iswindows: |
|
58 # no fork on Windows, but we can create a detached process |
|
59 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx |
|
60 # No stdlib constant exists for this value |
|
61 DETACHED_PROCESS = 0x00000008 |
|
62 _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP |
|
63 |
|
64 def runshellcommand(script, env): |
|
65 # we can't use close_fds *and* redirect stdin. I'm not sure that we |
|
66 # need to because the detached process has no console connection. |
|
67 subprocess.Popen( |
|
68 procutil.tonativestr(script), |
|
69 shell=True, env=procutil.tonativeenv(env), close_fds=True, |
|
70 creationflags=_creationflags) |
|
71 else: |
|
72 def runshellcommand(script, env): |
|
73 # double-fork to completely detach from the parent process |
|
74 # based on http://code.activestate.com/recipes/278731 |
|
75 pid = os.fork() |
|
76 if pid: |
|
77 # parent |
|
78 return |
|
79 # subprocess.Popen() forks again, all we need to add is |
|
80 # flag the new process as a new session. |
|
81 if sys.version_info < (3, 2): |
|
82 newsession = {'preexec_fn': os.setsid} |
|
83 else: |
|
84 newsession = {'start_new_session': True} |
|
85 try: |
|
86 # connect std* to devnull to make sure the subprocess can't |
|
87 # muck up these stream for mercurial. |
|
88 # Connect all the streams to be more close to Windows behavior |
|
89 # and pager will wait for scripts to end if we don't do that |
|
90 nullrfd = open(os.devnull, 'r') |
|
91 nullwfd = open(os.devnull, 'w') |
|
92 subprocess.Popen( |
|
93 procutil.tonativestr(script), |
|
94 shell=True, stdin=nullrfd, |
|
95 stdout=nullwfd, stderr=nullwfd, |
|
96 env=procutil.tonativeenv(env), |
|
97 close_fds=True, **newsession) |
|
98 finally: |
|
99 # mission accomplished, this child needs to exit and not |
|
100 # continue the hg process here. |
|
101 os._exit(0) |
|
102 |
51 |
103 class logtoprocessui(ui.__class__): |
52 class logtoprocessui(ui.__class__): |
104 def log(self, event, *msg, **opts): |
53 def log(self, event, *msg, **opts): |
105 """Map log events to external commands |
54 """Map log events to external commands |
106 |
55 |
131 ('OPT_{0}'.format(key.upper()), str(value)) |
80 ('OPT_{0}'.format(key.upper()), str(value)) |
132 for key, value in opts.iteritems()) |
81 for key, value in opts.iteritems()) |
133 env = dict(itertools.chain(procutil.shellenviron().items(), |
82 env = dict(itertools.chain(procutil.shellenviron().items(), |
134 msgpairs, optpairs), |
83 msgpairs, optpairs), |
135 EVENT=event, HGPID=str(os.getpid())) |
84 EVENT=event, HGPID=str(os.getpid())) |
136 runshellcommand(script, env) |
85 procutil.runbgcommand(script, env, shell=True) |
137 return super(logtoprocessui, self).log(event, *msg, **opts) |
86 return super(logtoprocessui, self).log(event, *msg, **opts) |
138 |
87 |
139 # Replace the class for this instance and all clones created from it: |
88 # Replace the class for this instance and all clones created from it: |
140 ui.__class__ = logtoprocessui |
89 ui.__class__ = logtoprocessui |