6

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 no 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...

BuvinJ
  • 10,221
  • 5
  • 83
  • 96
  • Have you solved the problem? – Silicomancer Apr 13 '19 at 11:21
  • No. Unfortunately. I'm going the kludge route... – BuvinJ Apr 13 '19 at 14:41
  • This is, in fact, going into an open source library I'm developing, which wraps QtIFW, along with a whole lot more. My wrapper is going to allow QtIFW to be used to produce cross platform "silent" installers that are command line driven. I want to provide options to auto uninstall prior installations, or exit with a failure indication if the program already is installed among other features. This silent option will allow for using ifw for mass installations, and non-gui environments such as terminal only servers. I can share the source when I'm done. – BuvinJ Apr 13 '19 at 14:47
  • Seems no solution is known. If you answer how you worked around the problem I can award you the bounty. – Silicomancer Apr 21 '19 at 08:39
  • Too bad no one knows the right way! My solution involves Python generating QScript (i.e JavaScript), which leans on Batch and Bash... I haven't tested the Bash yet, as I'm doing a chunk of work on Windows first, then I'll go resolve any minor kinks on Linux and Mac. I can post that current wok in progress (dropping the Python layer), and then post any revisions in the very near future... – BuvinJ Apr 21 '19 at 16:45
  • Are you running this in PowerShell on Windows Version 1903 by chance? – jmcker Jul 06 '19 at 21:59
  • This issue is QtIFW itself, not the shell it's running within. There is no built-in mechanism to return a value. – BuvinJ Jul 07 '19 at 16:04

1 Answers1

3

I found no real solution, so I did this instead...

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.

  • Paste the following into your QtIFW QScript:

This is the full 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 script, clear the log.

function Controller() {
    clearErrorLog();
   ...
}
  • For macOS or Linux (most distros ought to be happy at least):

Replace the first lines in clearErrorLog() with:

var path = installer.value( "errlog", "/tmp/installer.err" );
var deleteCmd = "rm \"" + path + "\";echo " + path;
var result = installer.execute( "sh", ["-c", deleteCmd] );

And replace the first lines in writeErrorLog() with:

var path = installer.value( "errlog", "/tmp/installer.err" );
var writeCmd = "echo " + msg + " > \"" + path + "\";echo " + path;
var result = installer.execute( "sh", ["-c", writeCmd] );

If you want to check for the platform, systemInfo.kernelType will return "winnt", "linux", or "darwin" (for macOS) among other possible values.

  • When you want to invoke the mechanism, call silentAbort( msg ).

In QtIWF scripting, there is no master exception handler, or whatnot, so as long as you don't have that silentAbort 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...).

BuvinJ
  • 10,221
  • 5
  • 83
  • 96