ronaldoussoren / py2app

py2app is a Python setuptools command which will allow you to make standalone Mac OS X application bundles and plugins from Python scripts.
Other
353 stars 35 forks source link

Allow multiple executables in a single app bundle #13

Closed ronaldoussoren closed 12 years ago

ronaldoussoren commented 13 years ago

Original report by dbrnz (Bitbucket: dbrnz, GitHub: dbrnz).


The following patch enables the name of the stub executable to determine the Python script to run. This allows multiple scripts to use a common runtime. Required manual steps are to make copies of the stub in ./MacOS and to copy the actual Python scripts into ./Resources.

diff --git a/py2app/apptemplate/src/main.c b/py2app/apptemplate/src/main.c
--- a/py2app/apptemplate/src/main.c
+++ b/py2app/apptemplate/src/main.c
@@ -1017,8 +1017,9 @@

     argv_new = alloca((argc + 1) * sizeof(char *));
     argv_new[argc] = NULL;
-    argv_new[0] = c_mainScript;
-    memcpy(&argv_new[1], &argv[1], (argc - 1) * sizeof(char *));
+    //argv_new[0] = c_mainScript;
+    //memcpy(&argv_new[1], &argv[1], (argc - 1) * sizeof(char *));
+    memcpy(argv_new, argv, argc * sizeof(char *));
     py2app_PySys_SetArgv(argc, argv_new);

diff --git a/py2app/bootstrap/boot_app.py b/py2app/bootstrap/boot_app.py
--- a/py2app/bootstrap/boot_app.py
+++ b/py2app/bootstrap/boot_app.py
@@ -7,7 +7,7 @@
     site.addsitedir(os.path.join(base, 'Python', 'site-packages'))
     if not scripts:
         import __main__
-    for script in scripts:
-        path = os.path.join(base, script)
-        sys.argv[0] = __file__ = path
-        execfile(path, globals(), globals())
+    script = sys.argv[0].split('/')[-1] + '.py'
+    path = os.path.join(base, script)
+    sys.argv[0] = __file__ = path
+    execfile(path, globals(), globals())
ronaldoussoren commented 13 years ago

Original comment by Ronald Oussoren (Bitbucket: ronaldoussoren, GitHub: ronaldoussoren).


I'm not to happy about the manual steps. It would be better to have a configuration option that enables this functionality, that way py2app could do the copies itself and it could make sure that all dependencies of the additional scripts are also present.

ronaldoussoren commented 13 years ago

Original comment by dbrnz (Bitbucket: dbrnz, GitHub: dbrnz).


Agree 100% that it would be better to have a configuration that enables this. At least there is a manual way to allow multiple scripts to show a common runtime...

ronaldoussoren commented 13 years ago

Original comment by Ronald Oussoren (Bitbucket: ronaldoussoren, GitHub: ronaldoussoren).


What is your usecase for this feature? I'm trying to design a good interface for this feature, but what's a good interface depends on what you try to do with it.

A possible interface for having embedded scripts in an application bundle:

setup(
   app=[
       dict(script="MyMain.py",  tools=["script1.py", "script2.py"])
   ])

This would create an MyMain.app bundle, and would also add 'script1' and 'script2' to MyMain.app/Contents/MacOS.

The usecase for this is an application that uses embedded scripts, or exposes some functionality using the command-line (and example of the latter is the "mate" command that's included with TextMate).

Another usecase for the feature you request is having multiple GUI applications that share a lot of code and/or resources. Something like this could be a good interface:

   setup(
       app=[
           dict(script="App1.py"), dict(script="App2.py"),
       ],
       options=dict(py2app=dict(
             share_resources=True,
       ))
   )

That would be something that's much harder to accomplish as building multiple app bundles is currently not supported at all. Building more complicated bundles would be useful though.

ronaldoussoren commented 13 years ago

Original comment by Anonymous.


I want to distribute several command line tools that all share a common library and other resources. My vote would be for "tools=[list]".

ronaldoussoren commented 13 years ago

Original comment by dbrnz (Bitbucket: dbrnz, GitHub: dbrnz).


I didn't intend to be anonymous above... :-)

ronaldoussoren commented 12 years ago

Original comment by Michael McCracken (Bitbucket: mikemccracken, GitHub: mikemccracken).


To add some info - I have both problems :)

I'd like to build a main app bundle that contains both a non-GUI 'tool', and a helper app that shows a window (but has LSUIElement=1)

So from this, it looks like I'll need multiple calls to setup() to build the two apps.

ronaldoussoren commented 12 years ago

Original comment by Andrew Barnert (Bitbucket: abarnert, GitHub: abarnert).


I have basically the first use case described in the 2011-06-14 comment.

My script needs to run a bunch of scripts via subprocess.Popen. They're all simple command-line scripts--no GUI, no PyObjC--but they pull in various standard and third party libraries that the parent doesn't.

I don't really care whether I get a wrapper executable for each one, or have to pass the script as argv[1] to the python executable. As long as the scripts end up in Resources, and their dependencies are bundled in, I'm happy.

I'm currently working around this by building a .app for each script, then copying the child.py, lib/python3.3/ and lib/python33.zip: into the equivalent locations in the parent app's Resources, and that works, but it's a bit clunky.

I'd be perfectly happy with the "tools=[list]" suggestion above.

ronaldoussoren commented 12 years ago

Original comment by Ronald Oussoren (Bitbucket: ronaldoussoren, GitHub: ronaldoussoren).


Add an option to insert additional (command-line) scripts into a bundle

The option "--extra-scripts=file1.py,file2.py" can be used to add extra command-line scripts to a plugin or application. The example above adds commands "file1" and "file2" to the Contents/MacOS directory of the bundle.

See issue #13 for more information

ronaldoussoren commented 12 years ago

Original comment by Ronald Oussoren (Bitbucket: ronaldoussoren, GitHub: ronaldoussoren).


I've implemented the "tools" option in changeset 488ec1001c42, although the option has a different name:

setup(
    name='BasicApp',
    app=['main.py'],
    options = {
        'py2app': {
             'extra_scripts': ['helper1.py', 'helper2.py']
        }
    }
)

This setup.py will add commands named 'helper1' and 'helper2' to BasicApp.app/Contents/MacOS (next to the launcher for the application). This also works for plugin bundles.

Embedding a second app, as Michael seems to require, is more complicated and can be added later. The complication is mostly in nicely specifying this in a setup.py file, py2app has some support for building multiple bundles in one go but AFAIK that functionality is untested and would need enhancement anyway (if the current functionality works it will create multiple independent (application) bundles).

ronaldoussoren commented 12 years ago

Original comment by Ronald Oussoren (Bitbucket: ronaldoussoren, GitHub: ronaldoussoren).


I'll track the embedding of apps inside other apps in issue #63.

ronaldoussoren commented 12 years ago

Original comment by Andrew Barnert (Bitbucket: abarnert, GitHub: abarnert).


EDIT: Scratch the previous comment. The --extra-scripts argument does work with setup.py, it's just I had to generate a new setup.py with 0.7 before it would accept the argument. Everything is fine.