Clarification on macOS bundle and exec icon on taskbar

8 views
Skip to first unread message

Gonzalo Garramuño

unread,
Nov 10, 2025, 5:24:01 PMNov 10
to fltkg...@googlegroups.com
Two questions:

1) According to some AI familiar with Apple, the:

/Applications/program.app/Contents/MacOS/program

program had to be a real DWARF executable, albeit on my old Intel
machine, on a simpler application, I can use an actual shell script. 
Has Apple tightened security or the AI is wrong?

2) Following the AI advice, when running the DWARF launcher program as
"program", if I use "execv" to call a shell script from it inside
"Contents/Resources/bin/launcher.sh", an exec terminal icon gets created
on the taskbar.  If launcher.sh is instead inside "Contents/MacOS", no
exec terminal icon appears.  Is this how it is supposed to work?


--
ggar...@gmail.com

Greg Ercolano

unread,
Nov 10, 2025, 7:03:50 PMNov 10
to fltkg...@googlegroups.com
On 11/10/25 14:23, Gonzalo Garramuño wrote:
Two questions:

1) According to some AI familiar with Apple, the:

/Applications/program.app/Contents/MacOS/program

program had to be a real DWARF executable, albeit on my old Intel machine, on a simpler application, I can use an actual shell script.  Has Apple tightened security or the AI is wrong? 


    Mmm, this sounds familiar.
    For my product, Rush, I include a bunch of submit scripts written in both python and perl.

    I found on macs one could not simply click from the Finder on a bare script and have it
    run properly, show an icon, etc.

    What I found was best was to make an Applications subdirectory in the directory with all
    the .py and .pl scripts, and in there, create a separate .app for each script.

    So if the script was foo.py, there was a ./Applications/foo.app bundle, and inside the MacOS
    directory was a copy of the same "wrapper binary" described above, which simply walks up
    the path from the executable to find a script of the same name in the directory containing
    the Applications subdir, and then exec'ed that.

    This seemed to give the best results, and once the script was running this way, the icon
    remained associated with it, and the script could run normally.

    The wrapper executable does some logic specific to my design requirements, but basically
    the main() does the following:

        1) Walks a list of interpreters based on the script's filename extension, e.g.

// Map filename extensions to interpreter commands
typedef struct {
    const char *ext;
    const char *cmd;
} Interpreter;

..

int main(..) {

    Interpreter interpreter[] = {
        // EXT      CMD
        // ---      -----------------------------
        { ".pl",   "perl" },
        { ".py",   "python" },
        { ".pyw",  "python" },
        { 0, 0 }
    };
..
    // SEARCH FOR PERL AND PYTHON SCRIPTS
    for ( int t=0; interpreter[t].ext; t++ )
    {
        const char *ext = interpreter[t].ext;
        const char *cmd = interpreter[t].cmd;

        if ( G_debug ) fprintf(stderr, "WORKING ON '%s' (%s):\n", cmd, ext);
	string filename;
	filename += string(argv[0]);  // "/path/to/foo.app/Contents/MacOS/foo"
	filename += string(ext);      // "/path/to/foo.app/Contents/MacOS/foo.pl"

        // TRY TO EXECUTE THE SCRIPT (IF IT EXISTS) IN VARIOUS WAYS
        //     On success, this will not return; script's interpreter replaces current process
        //
        Exec(filename.c_str(), ext, cmd);
    }
    ..fail message..
}

    The Exec() function above does a bit of work finding the script by first checking the same directory the wrapper executable is in (assumes full path in argv[0]) for a .pl/.py script of the same name as the .app.
If not found, walks up to just above the .app directory for the script, and if found, uses execlp(3) to run the interpreter with the script as the argument. If not found, looks for other extensions until all are exhausted.


    


Reply all
Reply to author
Forward
0 new messages