PDA

View Full Version : Calling external programs via QProcess on Mac OS



berliner
14th February 2011, 19:39
I'm quite new to MacOS and I'm trying to build my program on Mac OS for the first time. So far Windows and Linux were my target platforms. Everything works fine and I'm again impressed on the degree of platform independence that Qt offers.

But there is one problem:

I have a program (assume it's called foo) that attempts to call external tools (latex, gs) via QProcess. It should be quite straight forward and works fine on Windows and Linux



QProcess process;

process.start("latex ......");

bool success = process.waitForStarted();

if(!success)
{
int error = process.error();
qDebug("waitForStarted: error=%d\n", error);
....
}


On Mac OS that simply doesn't work when I launched my application (foo) via the bundle file (foo.app). process.start() returns false and the error code is 0. But it works fine when I start foo via the executable that is inside the bundle under foo.app/Contents/MacOS/foo

I notice that in that case a shell window pops up. Perhaps now it is executed in a different execution environment. Now foo can call all external tools.

Assuming it is perhaps the PATH variable from the environment, I tried to add the required path entries to the process environment with



QProcessEnvironment sysenv = QProcessEnvironment::systemEnvironment();
QString path = sysenv.value("PATH");
if(path != "")
path += ":";
path += "/usr/texbin:/usr/local/bin";

sysenv.remove("PATH");
sysenv.insert("PATH", path);

process.setProcessEnvironment(sysenv);


It still doesn't work. Even if it would, I would prefer not messing around with PATH values. How can I get the same execution execution environment as in the shell? Do I have to wrap my call inside a script and execute that via "sh"?

Why is a program started in a different context depending on whether the bundle or the exe from within the bundle is run?

Google didn't really help on that.

tatoo42
26th August 2011, 08:15
Hi,

I'm having exactly the same issue, did you manage to figure out?

Thanks

superpacko
22nd December 2011, 14:25
Well i had a similar problem in Mac. There's a really weird thing regarding enviroments on Mac. Running from Finder is totally diferent than running from console, and so is the excecution of the Apps and the actual exceutable inside MacOS.

The only way to make it work by launching from the .app is to set the paths of the external tools properly. We did a AppleScript that sets all the enviromental variables that we needed, and then calls the exceutable on Contents/MacOs/xxx to run on the background.
Thats really a windows approach adapted to Mac's architecture, as far as i know, mac handles all those things with the plist.info and files like those, inside that Contents/ structure.

Here's the applescript that we made:


tell application "Finder" to get folder of (path to me) as Unicode Text
set workingDir to POSIX path of result
do shell script "export PATH=$PATH:" & "workingDir & "/bin/; export DYLD=LIBRARY_PATH=& workingDir & "/bin/; cd " & workingDir & "/bin/MyProgram.app/Contents/MacOS/; ./MyProgram &"


1st line --> this finds the absolute path of where u are clicking on finder
2nd line --> saves the path to the workingDir variable
3rd line --> we call a shell script in a huge command (because if u do it in separate ways, it looses the previous), and we add the paths needed for execution, as well a dinamyc libraries, and finally call the executable on the background

If you dont want to run it from Finder, then u can take a simpler approach and create a simple bash script that adds those paths to the enviroment and then executes the .exe inside MacOS

Hope this helps.

berliner
1st December 2014, 19:45
I'm pulling this up, because I found another possible way of dealing with that problem. That "solution" would use the ability of app bundles to run binaries that exist witih the bundle.

If my app bundle contains...

./Foo.app/Contents/MacOS/foo
./Foo.app/Contents/MacOS/bar

...then the Foo.app can call the bar executable.

But I want to execute something outside i.e. ghostscript. So I created a symbolic link:
ln -s /usr/local/bin/gs ./Foo.app/Contents/MacOS/gs

Now Foo.app can execute gs without problem. As far as I can rely on /usr/local/bin/ to be the standard location, I can even distribute Foo.app with the symbolic link itside. If not, I would need some kind of installation step that involves creating the symbolic link. The question now is, if that is kind of acceptable as solution?

(and I still don't understand why Apple decided to implement that annoying limitation)

The weird thing is that the problem is almost unknown in Google. Seems as if Mac applications never have to call anything outside the bundle, which is kind of unlikely.

jefftee
3rd December 2014, 06:40
I use QProcess on the Mac to run ffprobe and ffmpeg successfully. All it took was to ensure that the PATH environmental variable contained the path for ffprobe and ffmpeg. Here's the code I use to prepend the PATH with /opt/local/bin, /usr/local/bin, and $HOME/bin, etc.



QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QStringList envlist = env.toStringList();
envlist.replaceInStrings(QRegularExpression("^(?i)PATH=(.*)"), "PATH=/opt/local/bin:/usr/local/bin:$HOME/bin:\\1");
QProcess ffprobe;
ffprobe.setEnvironment(envlist);


Hope that helps.

Jeff

berliner
6th December 2014, 21:12
Yes, I had that idea. I modify the PATH, I print it to confirm that it really got modifie, but it's still not working.

process.waitForStarted() just returns false and process.error() reports 0.

Did your case really work for an application bundle? It works for me too when I directly start the binary that resides inside the bundle. Thene the program seems to get the regular execution environment. But I want to deliver an application bundle, because it makes it easier to include the Qt libraries.

Meanwhile I created something in between. I used macdeployqt to create the bundle with all dependencies. Then I changed the bundles name and created a symbolic link in the top directory of the bundle that points to the binary under MacOS. The structure is then like this

/Foo.app/Contents/MacOS/foo
/Foo.app/foo -> Contents/MacOS/foo

Now the user can run the program without traversing down the directory structure. But I still don't like it.

jefftee
7th December 2014, 03:30
Since you ran macdeployqt against your bundle, it should have copied all of the Qt frameworks into your Foo.app/Contents/Frameworks directory. My guess is that the Qt framework libraries are not able to be located when you try to start the process.

Can you cd to your app bundle as follows and show output from otool?



cd /Applications/Foo.app/Contents/MacOS
otool -L foo


The commands above will show the library dependencies for your foo executable. Additionally, if you'll run the following, it will display the rpath(s) in your executed with the section identified by "LC_RPATH". First command switch was an uppercase "L", the one below is a lowercase "l":



otool -l foo


Thanks,

Jeff

berliner
7th December 2014, 20:45
Thanks for your efforts. Here is the output from otool.

otool -L


@loader_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.3.0, current version 5.3.1)
@loader_path/../Frameworks/QtGui.framework/Versions/5/QtGui (compatibility version 5.3.0, current version 5.3.1)
@loader_path/../Frameworks/QtCore.framework/Versions/5/QtCore (compatibility version 5.3.0, current version 5.3.1)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)


otool -l

(but, actually, there is not LC_RPATH section)



Load command 0
cmd LC_SEGMENT_64
cmdsize 72
segname __PAGEZERO
vmaddr 0x0000000000000000
vmsize 0x0000000100000000
fileoff 0
filesize 0
maxprot 0x00000000
initprot 0x00000000
nsects 0
flags 0x0
Load command 1
cmd LC_SEGMENT_64
cmdsize 712
segname __TEXT
vmaddr 0x0000000100000000
vmsize 0x00000000001ed000
fileoff 0
filesize 2019328
maxprot 0x00000007
initprot 0x00000005
nsects 8
flags 0x0
Section
sectname __text
segname __TEXT
addr 0x0000000100002690
size 0x000000000016196a
offset 9872
align 2^4 (16)
reloff 0
nreloc 0
flags 0x80000400
reserved1 0
reserved2 0
Section
sectname __stubs
segname __TEXT
addr 0x0000000100163ffa
size 0x00000000000015de
offset 1458170
align 2^1 (2)
reloff 0
nreloc 0
flags 0x80000408
reserved1 0 (index into indirect symbol table)
reserved2 6 (size of stubs)
Section
sectname __stub_helper
segname __TEXT
addr 0x00000001001655d8
size 0x0000000000001c8a
offset 1463768
align 2^2 (4)
reloff 0
nreloc 0
flags 0x80000400
reserved1 0
reserved2 0
Section
sectname __gcc_except_tab
segname __TEXT
addr 0x0000000100167264
size 0x000000000003ba30
offset 1471076
align 2^2 (4)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __const
segname __TEXT
addr 0x00000001001a2ca0
size 0x000000000001b608
offset 1715360
align 2^4 (16)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __cstring
segname __TEXT
addr 0x00000001001be2a8
size 0x000000000000c4e6
offset 1827496
align 2^0 (1)
reloff 0
nreloc 0
flags 0x00000002
reserved1 0
reserved2 0
Section
sectname __unwind_info
segname __TEXT
addr 0x00000001001ca78e
size 0x0000000000003cc4
offset 1877902
align 2^0 (1)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __eh_frame
segname __TEXT
addr 0x00000001001ce458
size 0x000000000001eba8
offset 1893464
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Load command 2
cmd LC_SEGMENT_64
cmdsize 792
segname __DATA
vmaddr 0x00000001001ed000
vmsize 0x0000000000198000
fileoff 2019328
filesize 57344
maxprot 0x00000007
initprot 0x00000003
nsects 9
flags 0x0
Section
sectname __program_vars
segname __DATA
addr 0x00000001001ed000
size 0x0000000000000028
offset 2019328
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __got
segname __DATA
addr 0x00000001001ed028
size 0x00000000000001f0
offset 2019368
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000006
reserved1 933 (index into indirect symbol table)
reserved2 0
Section
sectname __nl_symbol_ptr
segname __DATA
addr 0x00000001001ed218
size 0x0000000000000010
offset 2019864
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000006
reserved1 995 (index into indirect symbol table)
reserved2 0
Section
sectname __la_symbol_ptr
segname __DATA
addr 0x00000001001ed228
size 0x0000000000001d28
offset 2019880
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000007
reserved1 997 (index into indirect symbol table)
reserved2 0
Section
sectname __mod_init_func
segname __DATA
addr 0x00000001001eef50
size 0x0000000000000070
offset 2027344
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000009
reserved1 0
reserved2 0
Section
sectname __const
segname __DATA
addr 0x00000001001eefc0
size 0x0000000000008f38
offset 2027456
align 2^4 (16)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __data
segname __DATA
addr 0x00000001001f7f00
size 0x00000000000023fc
offset 2064128
align 2^4 (16)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __common
segname __DATA
addr 0x00000001001fa300
size 0x000000000018a650
offset 0
align 2^4 (16)
reloff 0
nreloc 0
flags 0x00000001
reserved1 0
reserved2 0
Section
sectname __bss
segname __DATA
addr 0x0000000100384950
size 0x00000000000000d3
offset 0
align 2^4 (16)
reloff 0
nreloc 0
flags 0x00000001
reserved1 0
reserved2 0
Load command 3
cmd LC_SEGMENT_64
cmdsize 72
segname __LINKEDIT
vmaddr 0x0000000100385000
vmsize 0x0000000000068000
fileoff 2076672
filesize 422208
maxprot 0x00000007
initprot 0x00000001
nsects 0
flags 0x0
Load command 4
cmd LC_DYLD_INFO_ONLY
cmdsize 48
rebase_off 0
rebase_size 0
bind_off 2076672
bind_size 9104
weak_bind_off 2085776
weak_bind_size 17032
lazy_bind_off 2102808
lazy_bind_size 28928
export_off 2131736
export_size 104432
Load command 5
cmd LC_SYMTAB
cmdsize 24
symoff 2240112
nsyms 5129
stroff 2329896
strsize 168984
Load command 6
cmd LC_DYSYMTAB
cmdsize 80
ilocalsym 0
nlocalsym 1217
iextdefsym 1217
nextdefsym 3072
iundefsym 4289
nundefsym 840
tocoff 0
ntoc 0
modtaboff 0
nmodtab 0
extrefsymoff 0
nextrefsyms 0
indirectsymoff 2322176
nindirectsyms 1930
extreloff 0
nextrel 0
locreloff 0
nlocrel 0
Load command 7
cmd LC_LOAD_DYLINKER
cmdsize 32
name /usr/lib/dyld (offset 12)
Load command 8
cmd LC_UUID
cmdsize 24
uuid 02E85858-CABA-39C5-A01A-FB2AE1929C24
Load command 9
cmd LC_VERSION_MIN_MACOSX
cmdsize 16
version 10.6
sdk 10.8
Load command 10
cmd LC_UNIXTHREAD
cmdsize 184
flavor x86_THREAD_STATE64
count x86_THREAD_STATE64_COUNT
rax 0x0000000000000000 rbx 0x0000000000000000 rcx 0x0000000000000000
rdx 0x0000000000000000 rdi 0x0000000000000000 rsi 0x0000000000000000
rbp 0x0000000000000000 rsp 0x0000000000000000 r8 0x0000000000000000
r9 0x0000000000000000 r10 0x0000000000000000 r11 0x0000000000000000
r12 0x0000000000000000 r13 0x0000000000000000 r14 0x0000000000000000
r15 0x0000000000000000 rip 0x0000000100002690
rflags 0x0000000000000000 cs 0x0000000000000000 fs 0x0000000000000000
gs 0x0000000000000000
Load command 11
cmd LC_LOAD_DYLIB
cmdsize 96
name @loader_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 5.3.1
compatibility version 5.3.0
Load command 12
cmd LC_LOAD_DYLIB
cmdsize 88
name @loader_path/../Frameworks/QtGui.framework/Versions/5/QtGui (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 5.3.1
compatibility version 5.3.0
Load command 13
cmd LC_LOAD_DYLIB
cmdsize 88
name @loader_path/../Frameworks/QtCore.framework/Versions/5/QtCore (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 5.3.1
compatibility version 5.3.0
Load command 14
cmd LC_LOAD_DYLIB
cmdsize 88
name /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 1.0.0
compatibility version 1.0.0
Load command 15
cmd LC_LOAD_DYLIB
cmdsize 80
name /System/Library/Frameworks/AGL.framework/Versions/A/AGL (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 1.0.0
compatibility version 1.0.0
Load command 16
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libstdc++.6.dylib (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 56.0.0
compatibility version 7.0.0
Load command 17
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libSystem.B.dylib (offset 24)
time stamp 2 Thu Jan 1 01:00:02 1970
current version 169.3.0
compatibility version 1.0.0
Load command 18
cmd LC_FUNCTION_STARTS
cmdsize 16
dataoff 2236168
datasize 3944
Load command 19
cmd LC_DATA_IN_CODE
cmdsize 16
dataoff 2240112
datasize 0

jefftee
9th December 2014, 06:08
I am not entirely sure this will help, but when macdeployqt is run on my system, my binary in the app bundle winds up with the Qt libraries specified as:



@rpath/Sparkle.framework/Versions/A/Sparkle (compatibility version 1.6.0, current version 1.8.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1265.21.0)
@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.3.0, current version 5.3.2)
@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui (compatibility version 5.3.0, current version 5.3.2)
@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore (compatibility version 5.3.0, current version 5.3.2)
@executable_path/../Frameworks/QtConcurrent.framework/Versions/5/QtConcurrent (compatibility version 5.3.0, current version 5.3.2)
@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork (compatibility version 5.3.0, current version 5.3.2)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 855.17.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1056.16.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)


Can you try using the following linker flags and see if the behavior works for you?



QMAKE_LFLAGS += -Wl,-install_name,@executable_path/../Frameworks


Not sure it will help your situation, but I've never used @loader_path, so might be worth a shot...

Regards,

Jeff

berliner
11th December 2014, 00:44
I just installed Qt 5.4.0 and rebuilt everything. Now the output after applying macdeployqt is slightly different:



@executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets (compatibility version 5.4.0, current version 5.4.0)
@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui (compatibility version 5.4.0, current version 5.4.0)
@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore (compatibility version 5.4.0, current version 5.4.0)
/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 56.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)


Now it's also using @executable_path, but when I start the bundle by double-clicking, the program still cannot call external programs. I tried something I didn't before and ran the bundle from the shell with "open Foo.app" and being started like that, the program can do everything. So, it's definitely the execution enviroment. When I use MuCommander to browse the bundle for the binary, that is hidden inside, and run that, then it also opens a shell window and everything is fine.

Somehow I have to find out how to spawn a shell execution environment when running a bundle.

Maybe that article points to the right direction: http://stupidpythonideas.blogspot.de/2014/07/mac-environment-variables.html


On OS X, Finder.app is a child of your launchd session, which is a child of the root launchd session, which is process 0. There's no shell anywhere. And when you double-click an app, it asks LaunchServices to run the app. So, your app doesn't inherit anything from Finder, and even if it did, Finder hasn't inherited anything from your login shell.

He suggests defining an environment in the plist file with the LSEnvironment key. The question is, what has to be there in order to mimick a shell environment. Can be quite a lot I guess.

jefftee
11th December 2014, 01:20
Somehow I have to find out how to spawn a shell execution environment when running a bundle.

A couple of things I forgot to ask up front (and probably should have):

1) Have you tried to start the QProcess with the fully qualified path for latex? i.e. /usr/local/bin/latex (or wherever it is installed)

2) Run otool -L latex to see what external library dependencies latex may have. If unable to load, the start would fail, etc.

3) I don't have latex installed on my mac, but can you confirm it's a binary executable by running "file latex"? Should show as Mach-O 64-bit executable x86_64. On the off chance that it's a shell script, I believe you have to run the shell scripts by starting the program "sh" or "bash" (or whatever shell you use) and pass latex as the first parameter to the QProcess start (i.e. "sh latex", etc)

Look forward to your responses, I'm curious to understand why this isn't working because in my usage, I had no problems at all running external processes using QProcess. One difference I do notice is that you seem to be using the start method that takes the program and all arguments as one QString. In my case, use use the overload that takes a QString for the program to start and a QStringList that contains all of the program arguments. I'm using ffmpeg, which takes a lot of arguments, so that's why I chose that overload, but might be worth a try for you to do the same.

berliner
14th December 2014, 01:07
Ok, I tried running latex and gs with full paths and that is working.

I just don't understand why it didn't work with just adding the paths to the PATH variable by modifying the variable list as you suggested.

I add /usr/texbin and /usr/local/bin and I confirm that both have been added by printing the whole string list.

original
PATH=/Applications/Qt5.4.0/5.4/clang_64/bin:/usr/bin:/bin:/usr/sbin:/sbin

modifed
PATH=/usr/texbin:/usr/local/bin:/Applications/Qt5.4.0/5.4/clang_64/bin:/usr/bin:/bin:/usr/sbin:/sbin

Then I apply it to the process by using setEnvironment(...) right before calling process.start(). Should be working, but isn't. I think I will now extend the commands with the full path and give the user an option to configure that path via the program settings.

Added after 1 40 minutes:

Could it actually be an error in thinking? I don't have to change the environment of the new process but of the current process? Because this is the one that wants to start the program.

jefftee
14th December 2014, 05:07
Could it actually be an error in thinking? I don't have to change the environment of the new process but of the current process? Because this is the one that wants to start the program.

I think you hit the nail on the head, and here's why it was working for me w/o any issues. I do get the fully qualified path to the ffprobe and ffmpeg executables via a user preferences dialog that uses QSettings.

In my method that reads user prefs via QSettings, I add "/opt/local/bin:/usr/local/bin" to the path for QProcess, and I actually run a "which" command with ffprobe and ffmpeg as arguments to the "which" command so that I can have a default value for the QSettings for ffprobe and ffmpeg. If I do not update the PATH before running "which", it fails to find ffprobe and ffmpeg, because as you have pointed out, my gui app running from the bundle still has the default path.

So, you may want to try something similar, update your path to contain the directories you want, then start a QProcess for "which latex". The output of that QProcess will contain the full path to latex, etc.

Glad you raised that question, because I it was bugging me... :)

Good luck,

jthomps

berliner
15th December 2014, 21:35
I have one more take on that.

The Apple developer docs describe how to set env variables through the Info.plist file by adding an LSEnvironment section (https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/PlistBuddy.8.html).

I added that to Info.plist:



<key>LSEnvironment</key>
<dict>
<key>PATH</key>
<string>/usr/texbin:/usr/local/bin</string>
</dict>


...but it didn't work. I also tried <string>$PATH:/usr/texbin:/usr/local/bin</string> and some other variants, including adding some arbitrarily named variables just to see if ANYTHING will be added to the environment. No it didn't. I googled around and it seems that others ran into the same problem. It just doesn't seem to be working as promised by Apple.

I found something else that works and that is modifying the PATH with good old C funcion

int setenv(const char *name, const char *value, int overwrite);

So, my code looks like that now:


void setMacEnvironment()
{
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QStringList envlist = env.toStringList();

for(int i=0; i < envlist.size(); i++)
{
QString entry = envlist[i];

if(entry.startsWith("PATH="))
{
int index = entry.indexOf("=");

if(index != -1)
{
QString value = entry.right(entry.length() - (index+1));
value += ":/usr/texbin:/usr/local/bin";

setenv("PATH", value.toLatin1().constData(), true);
}

break;
}
}
}


(ok can be more sophisticated using iterator instead of array subscript and string splitting by regular expression, but for me it's ok.)

I will probably add a program setting that allows Mac users to set a string they want to be added to PATH. The default value will be "/usr/texbin:/usr/local/bin".

Of course, I could also just call the external programs with their absolut paths, but setenv() has the advantage that the modified environment is inherited by these programs. If these again need to call something external, they will have the necessary PATH already set up. That is actually my case. My program calls latex and gs, but it also spawns another tool that again has to call gs. Then it comes in handy that my program already creates a working enviroment for all forks and forks of forks.

One more little addition for Windows compatibility. The stdlib.h that ships with Visual Studio does not contain setenv(). I think that's because setenv is BSD and POSIX.1-2001, but Windows only implements POSIX.1 So I'm using the following:



#ifdef WIN32
SetEnvironmentVariable("PATH", value.toLatin1().constData());
#else
setenv("PATH", value.toLatin1().constData(), true);
#endif;


Works for Visual Studio and probably for QtCreator when Visual Studio Express is used as the compiler.