I want to abort a Qt Installer Framework process, which can be done in multiple ways (follow the examples Qt provides for this, or throw an uncaught exception, or just inject an invalid command to cause a hard crash...). But now matter what, it seems like the exit code is 0 (i.e. "success")! Is there any way to make it 1 (or something else)?

I can think of some ugly, kludgy alternatives for programmatically evaluating the success of this process, but I really hope that's not necessary...

1 Answers

2
BuvinJ On Best Solutions

As stated in the comment under my answer, this a work in progress. I'll post the rest soon. I actually generate this script via Python on Windows. On Mac and Linux, it will be slightly different. It would be quite easy to combine the variations, but for my purposes I was thinking about generating them independently. (I may rethink that though...)

Basically, my workaround is to throw an uncaught exception, and to write an error log file under "exit on failure" conditions. The presence of the log indicates that a problem occurred. If there is no such log, then the installation was successful. If you only care about pass/fail, just check for the file existence. Else, read the file for the details.

In my solution, I allow the client to pass an argument to the installer, specifying a specific path / name for the error log. If that is not provided, it writes to a default path on the temp directory.

Note that unless I'm wrong, I don't see a means for directly writing files, deleting them, or resolving temp paths in a straight forward manner within QtIFW scripting. So, I just use shell operations. The way I use them, I return back the path resolutions from the shell, thereby implicitly allowing the use of environmental variables in the file path.

Again, for now this is the Windows specific solution:

function clearErrorLog() {
    var path = installer.value( "errlog", "%temp%\\installer.err" );
    var deleteCmd = "echo off && del \"" + path + "\" /q\necho " + path + "\n";
    var result = installer.execute( "cmd.exe", ["/k"], deleteCmd );
    if( result[1] != 0 ) 
        throw new Error("Clear error log failed.");
    try{
        var cmdOutLns = result[0].split("\n");
        path = cmdOutLns[cmdOutLns.length-2].trim();
    }
    catch(e){ path = ""; }
    if( path=="" || installer.fileExists( path ) ) 
        throw new Error("Clear error log failed. (file exists)");
    console.log("Cleared error log: " + path);
}

function writeErrorLog( msg ) {
    var path = installer.value( "errlog", "%temp%\\installer.err" );
    var writeCmd = "echo off && echo " + msg + " > \"" + path + "\"\necho " + path + "\n";
    var result = installer.execute( "cmd.exe", ["/k"], writeCmd );
    if( result[1] != 0 ) 
        throw new Error("Write error log failed.");
    try{
        var cmdOutLns = result[0].split("\n");
        path = cmdOutLns[cmdOutLns.length-2].trim();
    }
    catch(e){ path = ""; }
    if( path=="" || !installer.fileExists( path ) ) 
        throw new Error("Write error log failed. (file does not exists)");
    console.log("Wrote error log to: " + path);
}

function silentAbort( msg ) {
    writeErrorLog( msg );
    throw new Error( msg );
}

At the start of the Controller script, clear the log.

function Controller() {
    clearErrorLog();
   ...
}

When you want to invoke the mechanism, call silentAbort( msg ). In QtIWF scripting, there is no master exception handler, or whatnot, so as long you don't have that function nested inside a try block, the Exception will effectively terminate the program immediately.

In order to explicitly state what path to use, launch the installer with an "errlog" key as an argument like this: myinstaller.exe errlog=mylog.err. If you don't include a full path, the current working directory will be used.

It's up to you to delete the log if you want. It's pretty common to also leave garbage in the temp directory, of course (not that I'm really a fan of that...).