qtqt-installer

Can Qt Installer Framework return a failure exit code?


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


Solution

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

    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();
       ...
    }
    

    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.

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