RFC: Alternative for exemaker

Adrian Buehlmann adrian at cadifra.com
Sat Jun 23 06:59:29 CDT 2012


On 2012-06-23 13:24, Andrei Polushin wrote:
> 23.06.2012 16:13, Adrian Buehlmann wrote:
>> 	Py_Initialize();
>> 	ret = Py_Main(n, pyargv);
>> 	Py_Finalize();
>> 	return ret;
> 
> In Python 2.7, main is implemented this way[1]:
> 
>     int main(int argc, char **argv)
>     {
>         return Py_Main(argc, argv);
>     }
> 
> So, Py_Initialize() is not required.
> 
> [1] http://hg.python.org/cpython/file/70274d53c1dd/Modules/python.c
> 
> 
> As for me, I don't use hackable Mercurial, but I have had the related
> problem when I install Mercurial with using setup.py into a shared
> Python installation.
> 
> Notice that I'm on Windows, and the installation creates `hg.bat' in
> PYTHON_HOME/Scripts directory. A few months ago, I've noticed that this
> configuration prevents the Mercurial to work in command server mode,
> because the command server mode clients expect the existence of `hg.exe'.
> 
> So my guess that not only the Hackable-Mercurial, but also the default
> installation should install a little .exe instead of .bat on Windows!
> 
> Well, that was slightly off-topic, but the above motivated me to create
> short hg.exe similar to yours.
> 
> My version has the following properties:
> 
>  1) It doesn't link to shlwapi.dll
>  2) It assumes the script name is the same as .exe name, but
>     without the .exe extension (.py extension is not appended)
>  3) It allocates a copy of argv on stack, so the number of arguments
>     is not limited by a constant.
> 
> The code is as follows:
> 
>     #define _CRT_SECURE_NO_DEPRECATE
>     #include <malloc.h>
>     #include <stdlib.h>
>     #include <string.h>
>     #include <windows.h>
> 
>     #include <Python.h>
> 
>     int main(int argc, char* argv[])
>     {
>         char script[_MAX_PATH];
>         GetModuleFileName(NULL, script, _MAX_PATH);
> 
>         char* dot = strrchr(script, '.');
>         if (!dot) {
>             return -1;
>         } else {
>             *dot = 0; // cut ".exe"
>         }
> 
>         char** args = (char**)_alloca((argc + 2) * sizeof(char*));
>         if (!args) {
>             return -2;
>         } else {
>             args[0] = argv[0];
>             args[1] = script;
>             for (int i = 1; i <= argc; ++i) {
>                 args[i + 1] = argv[i];
>             }
>         }
> 
>         return Py_Main(argc + 1, args);
>     }
> 
> It's worth noting that I link hg.exe to the same runtime as used by the
> corresponding Python version. For Python 2.7, it is MSVCR90.DLL


Very interesting. Thanks for the info! I see what I can merge from your bits.


FWIW, my hg.exe (like python.exe itself) *does* have a dependency for
MSVCR90.DLL in the embedded manifest (see also 
https://bitbucket.org/abuehl/hgexe/raw/4ed0e506e370/Makefile.nmake):

  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="amd64" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>

(copied from the contents of the built hg.exe).


In the mean time, I think I found a solution for the spawndetached problem

 ( http://selenic.com/pipermail/mercurial-devel/2012-June/041925.html )

with

https://bitbucket.org/abuehl/hgexe/changeset/4ed0e506e370/raw

# HG changeset patch
# User Adrian Buehlmann <adrian at cadifra.com>
# Date 1340450994 -7200
# Node ID 4ed0e506e3708fba5483465e1b3bd8c6fbd99fe6
# Parent  a649fb7195813f0c6057566ac7966bfb2a7d0fd6
solve spawndetached problem

Fixes

  $ hg --traceback serve -n test -p 20070 -d --pid-file=hg.pid -E errors.log
  Traceback (most recent call last):
    File "C:\Users\adi\hgrepos\hg-main\mercurial\dispatch.py", line 88, in _runcatch
      return _dispatch(req)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\dispatch.py", line 739, in _dispatch
      cmdpats, cmdoptions)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\dispatch.py", line 513, in runcommand
      ret = _runcommand(ui, options, cmd, d)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\dispatch.py", line 829, in _runcommand
      return checkargs()
    File "C:\Users\adi\hgrepos\hg-main\mercurial\dispatch.py", line 800, in checkargs
      return cmdfunc()
    File "C:\Users\adi\hgrepos\hg-main\mercurial\dispatch.py", line 736, in <lambda>
      d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\util.py", line 475, in check
      return func(*args, **kwargs)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\extensions.py", line 139, in wrap
      util.checksignature(origfn), *args, **kwargs)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\util.py", line 475, in check
      return func(*args, **kwargs)
    File "C:\Users\adi\hgrepos\hg-main\hgext\mq.py", line 3505, in mqcommand
      return orig(ui, repo, *args, **kwargs)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\util.py", line 475, in check
      return func(*args, **kwargs)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\commands.py", line 5120, in serve
      cmdutil.service(opts, initfn=service.init, runfn=service.run)
    File "C:\Users\adi\hgrepos\hg-main\mercurial\cmdutil.py", line 486, in service
      raise util.Abort(_('child process failed to start'))
  Abort: child process failed to start
  abort: child process failed to start

diff --git a/hg.c b/hg.c
--- a/hg.c
+++ b/hg.c
@@ -30,9 +30,24 @@
 	PathRemoveFileSpec(scriptname);
 	strcat_s(scriptname, sizeof(scriptname), "\\hg.py");
 
+	/*
+	Start assembling the args for the Python interpreter call. We put the
+	name of our exe in the position where the python.exe canonically is.
+	*/
 	n = 0;
 	pyargv[n++] = exename;
-	pyargv[n++] = scriptname;
+	/* 
+	Add the scriptname, if it's not already there. It may already be
+	there, if mercurial spawned a child process of itself, in the same way
+	as it got called, that is, with the scriptname already in place. So
+	we optionally accept the scriptname as the first argument, letting
+	our exe taking the role of the python interpreter.
+	*/
+	if (argc > 0 && strcmp(argv[1], scriptname) != 0) {
+		pyargv[n++] = scriptname;
+	}
+
+	/* copy remaining args from the command line */
 	for (i = 1; i < argc; i++) {
 		if (i > _NUM_ARGS) {
 			fprintf(stderr, "abort: too many arguments\n");
@@ -42,7 +57,7 @@
 	}
 
 	Py_Initialize();
-	ret = Py_Main(n, pyargv);
+	ret = Py_Main(n, pyargv); /* The Python interpreter call */
 	Py_Finalize();
 	return ret;
 }


More information about the Mercurial-devel mailing list