Skip to content

Commit

Permalink
Added mechanism for creating temp "flag" files for QtIFW operations, …
Browse files Browse the repository at this point in the history
…driven by dynamic values in the QtScript.
  • Loading branch information
BuvinJ committed Jan 8, 2021
1 parent 234496c commit 9a4101e
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 36 deletions.
96 changes: 80 additions & 16 deletions distbuilder/qt_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
QT_IFW_ASKPASS_PLACEHOLDER = "[%s]" % (QT_IFW_ASKPASS_KEY,)
QT_IFW_ASKPASS_TEMP_FILE_PATH = "/tmp/{0}.path".format( QT_IFW_ASKPASS_KEY )

QT_IFW_UNDEF_VAR_VALUE = "undef"

QT_IFW_DYNAMIC_SYMBOL = "@"

QT_IFW_ROOT_DIR = "@RootDir@"
Expand Down Expand Up @@ -1740,7 +1742,8 @@ def _genLib( self ):
TAB + 'if( !varNames ) varNames = dynamicVars' + END +
TAB + 'for( var i=0; i != varNames.length; ++i ) ' + SBLK +
(2*TAB) + 'var varName = varNames[i]' + END +
(2*TAB) + 'var varVal = installer.value( varName, "?" )' + END +
(2*TAB) + 'var varVal = installer.value( varName, "' +
QT_IFW_UNDEF_VAR_VALUE + '" )' + END +
(2*TAB) + 'ret = ret.split( "@" + varName + "@" ).join( varVal )' + END +
(2*TAB) + EBLK +
TAB + 'return ret' + END +
Expand Down Expand Up @@ -2046,7 +2049,8 @@ def _genLib( self ):
(2*TAB) + '"oFile.Close\\n" ' + END +
TAB + 'for( var i=0; i != varNames.length; ++i ) ' + SBLK +
(2*TAB) + 'var varName = varNames[i]' + END +
(2*TAB) + 'var varVal = Dir.toNativeSeparator( installer.value( varName, "?" ) )' + END +
(2*TAB) + 'var varVal = Dir.toNativeSeparator( installer.value( varName, "' +
QT_IFW_UNDEF_VAR_VALUE + '" ) )' + END +
(2*TAB) + 'if( isDoubleBackslash ) varVal = varVal.replace(/\\\\/g, \'\\\\\\\\\')' + END +
(2*TAB) + 'vbs += "sText = Replace(sText, Amp + \\"" + varName + "\\" + Amp, \\"" + varVal + "\\")\\n"' + NEW +
TAB + EBLK +
Expand Down Expand Up @@ -5151,9 +5155,11 @@ def opDataPath( rootFileName, isNative=True,
return path

@staticmethod
def CreateOpFlagFile( event, fileName, isElevated=True ):
def CreateOpFlagFile( event, fileName, dynamicVar=None,
isElevated=True ):
return QtIfwExternalOp.__genScriptOp( event,
script=QtIfwExternalOp.WriteOpDataFileScript( fileName ),
script=QtIfwExternalOp.CreateOpFlagFileScript(
fileName, dynamicVar ),
isElevated=isElevated )

@staticmethod
Expand Down Expand Up @@ -5237,18 +5243,12 @@ def CreateStartupEntry( pkg=None,
util._onPlatformErr()

if IS_WINDOWS:
# TODO: Expand upon registry functions

# TODO: Deal with 64 bit vs 32 bit registry contexts
# Allow the use of either literal paths or implicit wow64 resolution
# Some thoughts:
# https://stackoverflow.com/questions/630382/how-to-access-the-64-bit-registry-from-a-32-bit-powershell-instance


@staticmethod
def CreateWindowsAppFoundTempFile( event, appName, fileName,
def CreateWindowsAppFoundFlagFile( event, appName, fileName,
isAutoBitContext=True ):
return QtIfwExternalOp.__genScriptOp( event,
script=QtIfwExternalOp.CreateWindowsAppFoundTempFileScript(
script=QtIfwExternalOp.CreateWindowsAppFoundFlagFileScript(
appName, fileName, isAutoBitContext ),
isReversible=False, isElevated=True )

Expand All @@ -5265,7 +5265,8 @@ def UninstallWindowsApp( event, appName, arguments=None,
op.successRetCodes=[ 0, QtIfwExternalOp.__NOT_FOUND_EXIT_CODE ]
op.uninstRetCodes =[ 0, QtIfwExternalOp.__NOT_FOUND_EXIT_CODE ]
return op


# TODO: Expand upon registry functions (notably with cascading scripts)
@staticmethod
def CreateRegistryKey( event, key, isAutoBitContext=True ):
return QtIfwExternalOp.__genScriptOp( event,
Expand Down Expand Up @@ -5409,6 +5410,19 @@ def __genScriptOp( event, script, uninstScript=None,
isElevated=isElevated,
externalRes=externalRes if externalRes else [] )


@staticmethod
def CreateOpFlagFileScript( fileName, dynamicVar=None ): # TODO: Test in NIX/MAC
return ExecutableScript( QtIfwExternalOp.__scriptRootName(
"createOpFlagFile" ), script=(
("" if dynamicVar is None else
QtIfwExternalOp.__batExitIfFalseVar( dynamicVar )
if IS_WINDOWS else
QtIfwExternalOp.__shExitIfFalseVar( dynamicVar ) ) +
str( QtIfwExternalOp.WriteOpDataFileScript(
fileName, data=None ) )
))

# TODO: Auto handle escape sequences?
@staticmethod
def WriteOpDataFileScript( fileName, data=None ): # TODO: Test in NIX/MAC
Expand Down Expand Up @@ -5495,15 +5509,48 @@ def RunProgramScript( path, arguments=None, isAutoQuote=True,
if IS_WINDOWS:

@staticmethod
def __batExitIfFileMissing( fileName, isNegated=False, errorCode=0 ):
def __batExitIfFalseVar( varName, isNegated=False, errorCode=0 ):
return str( ExecutableScript( "", script=([
'set "reqFlag={varName}"'
, 'if "%reqFlag%"=="{undef}" set "reqFlag=false"'
, 'if "%reqFlag%"=="" set "reqFlag=false"'
, 'if "%reqFlag%"=="0" set "reqFlag=false"'
, 'if {negate}"%reqFlag%"=="false" exit /b {errorCode}'
]), replacements={
'negate' : ('' if isNegated else 'not ')
, 'varName' : qtIfwDynamicValue( varName )
, 'errorCode': errorCode
, 'undef' : QT_IFW_UNDEF_VAR_VALUE
}) )

@staticmethod
def __batExitIfFileMissing( fileName, isNegated=False, errorCode=0 ):
return str( ExecutableScript( "", script=([
'if {negate}exist "{filePath}" exit /b {errorCode}'
]), replacements={
'negate' : ('' if isNegated else 'not ')
, 'filePath' : QtIfwExternalOp.opDataPath( fileName )
, 'errorCode': errorCode
}) )

# TODO: TEST
@staticmethod
def __psExitIfFalseVar( varName, isNegated=False, errorCode=0 ):
return str( ExecutableScript( "", script=([
'$reqFlag="{varName}"'
, 'if( $reqFlag -eq "{undef}" ) { $reqFlag="false" }'
, 'if( $reqFlag -eq "" ) { $reqFlag="false" }'
, 'if( $reqFlag -eq "0" ) { $reqFlag="false" }'
, 'if( {negate}($reqFlag -eq "false") ) {'
, ' [Environment]::Exit( {errorCode} )'
, '}'
]), replacements={
'negate' : ('' if isNegated else '! ')
, 'varName' : qtIfwDynamicValue( varName )
, 'errorCode': errorCode
, 'undef' : QT_IFW_UNDEF_VAR_VALUE
}) )

@staticmethod
def __psExitIfFileMissing( fileName, isNegated=False, errorCode=0 ):
return str( ExecutableScript( "", script=([
Expand Down Expand Up @@ -5584,7 +5631,7 @@ def __psFindWindowsAppUninstallCmd( appName, isAutoBitContext,
} ) )

@staticmethod
def CreateWindowsAppFoundTempFileScript( appName, fileName,
def CreateWindowsAppFoundFlagFileScript( appName, fileName,
isAutoBitContext=True ):
psScriptTemplate=(
QtIfwExternalOp.__psFindWindowsAppUninstallCmd(
Expand Down Expand Up @@ -5939,6 +5986,23 @@ def ReplacePrimaryIconInExeScript( exePath, iconDirPath,

if IS_MACOS or IS_LINUX:

# TODO: TEST
@staticmethod
def __shExitIfFalseVar( varName, isNegated=False, errorCode=0 ):
return str( ExecutableScript( "", script=([
'reqFlag="{varName}"'
, '[ "{reqFlag}"=="{undef}" ] && reqFlag="false"'
, '[ "{reqFlag}"=="" ] && reqFlag="false"'
, '[ "{reqFlag}"=="0" ] && reqFlag="false"'
, '[ {negate}"{reqFlag}"=="false" ] && exit {errorCode}'
]), replacements={
'negate' : ('' if isNegated else '! ')
, 'varName' : qtIfwDynamicValue( varName )
, 'errorCode': errorCode
, 'undef' : QT_IFW_UNDEF_VAR_VALUE
}) )

# TODO: TEST
@staticmethod
def __shExitIfFileMissing( fileName, isNegated=False, errorCode=0 ):
if not fileName: return ""
Expand Down
27 changes: 25 additions & 2 deletions docs/ConfigClasses.md
Original file line number Diff line number Diff line change
Expand Up @@ -818,9 +818,17 @@ doubling them up.
On Windows, set `isAutoBitContext=False` if you need to execute a 64 bit
program from the installer's 32 bit context.

#### QtIfwExternalOp.CreateOpFlagFile
#### QtIfwExternalOp.CreateOpFlagFile

CreateOpFlagFile( event, fileName, isElevated=True )
CreateOpFlagFile( event, fileName, dynamicVar=None, isElevated=True )

**dynamicVar**: Control the creation of this file via a dynamic installer
variable. Note that a "false" condition will be met when a boolean variable is
explicitly set to `false`, or if the variable is undefined, or it contains no
content, or if it is set to `0`. The opposing `true` condition, required to
create the flag file, is simply the inverse of that. By default, this argument
is set to `None`, and therefore the file will be created without any logical
controls.

#### QtIfwExternalOp.WriteOpDataFile

Expand Down Expand Up @@ -936,6 +944,21 @@ program from the installer's 32 bit context.
Note: Elevation is controlled via the operation executing the script
rather embedded within it.

#### QtIfwExternalOp.CreateOpFlagFileScript

CreateOpFlagFile( fileName, dynamicVar=None )

**dynamicVar**: Control the creation of the flag file via a dynamic installer
variable. Note that a "false" condition will be met when a boolean variable is
explicitly set to `false`, or if the variable is **undefined**, or it contains **no content**, or if it is set to `0`. The opposing `true` condition, required
to create the flag file, is simply the inverse of that. So notably, the flag
can be raised by having a value in the dynamic variable, to then just simply
"true". By default, this argument is set to `None`, and therefore the file will be created without any logical
controls.

Note that the flag is either raised by this function or not. This function **does
not remove** a file that already exists.

#### QtIfwExternalOp.WriteOpDataFileScript

WriteOpDataFileScript( fileName, data=None )
Expand Down
52 changes: 34 additions & 18 deletions examples/hello_qtifw_ops/cascading_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,18 @@ def customizeFinishedPage( cfg ):
)

# A boolean state may be represented via a file's existence,
# created (or not) during an installer operation...
def setBoolCascadeOp( fileName ):
return QtIfwExternalOp.CreateOpFlagFile( ON_INSTALL, fileName )
# created (or not) during an installer operation...
# If optionally passing a variable name to CreateOpFlagFile, the
# operation will pivot upon the boolean evaluation of that variable
def setBoolCascadeOp( fileName, varName=None ):
return QtIfwExternalOp.CreateOpFlagFile( ON_INSTALL,
fileName, varName )

# A boolean state may be evaluated via a file's existence,
# during a subsequent installer operation...
def getBoolCascadeOp( fileName, destFilePath ):
createFileScript = ExecutableScript( "createBoolEvalFile", script=([
createFileScript = ExecutableScript( "%sEvalFile" % (fileName,),
script=([
'set "msg=FALSE"'
, 'if exist "{srcFilePath}" set "msg=TRUE"'
, 'echo %msg% > "{destFilePath}"'
Expand All @@ -62,11 +66,11 @@ def getBoolCascadeOp( fileName, destFilePath ):
uninstScript=QtIfwExternalOp.RemoveFileScript( destFilePath ) )

# An installer variable may be set somewhere in the QtScript...
def setInstallerVaribale( pkgScript, varName ):
def setInstallerVaribale( pkgScript, varName, value ):
# Package Script preOpSupport is a convenient place to test this
# Change the value assigned to test the results...
pkgScript.preOpSupport = QtIfwPackageScript.setValue(
varName, QT_IFW_HOME_DIR )
varName, value )

# An installer variable can be written to a data file...
def setVaribaleCascadeOp( fileName, varName ):
Expand Down Expand Up @@ -107,7 +111,7 @@ def getVaribaleCascadeOp( fileName, destFilePath ):
# manner) maybe applied via these abstractions.
if IS_WINDOWS:
def setAppFoundFileOp( event, appName, is32BitRegistration, fileName ):
return QtIfwExternalOp.CreateWindowsAppFoundTempFile( event,
return QtIfwExternalOp.CreateWindowsAppFoundFlagFile( event,
appName, fileName, isAutoBitContext=is32BitRegistration )

def launchAppIfFound( event, exePath, appFoundFileName ):
Expand All @@ -117,23 +121,35 @@ def launchAppIfFound( event, exePath, appFoundFileName ):
runConditionFileName=appFoundFileName,
isRunConditionNegated=False )

BOOL_FILE_NAME = "boolData"
BOOL_EVAL_FILE_NAME = "cascadingBool.txt"
VAR_NAME = "myDynamicPath"
VAR_FILE_NAME = "varData"
VAR_FETCH_FILE_NAME = "cascadingVar.txt"
TIME_FILE_NAME = "timeData"
TIME_FETCH_FILE_NAME = "cascadingTime.txt"
STATIC_BOOL_FILE_NAME = "staticBoolData"
STATIC_BOOL_EVAL_FILE_NAME = "staticCascadingBool.txt"
DYNAMIC_BOOL_FILE_NAME = "dynamicBoolData"
DYNAMIC_BOOL_EVAL_FILE_NAME = "dynamicCascadingBool.txt"
VAR_NAME = "myDynamicPath"
VAR_FILE_NAME = "varData"
VAR_FETCH_FILE_NAME = "cascadingVar.txt"
TIME_FILE_NAME = "timeData"
TIME_FETCH_FILE_NAME = "cascadingTime.txt"

customizeFinishedPage( cfg )
pkg = cfg.packages[0]
setInstallerVaribale( pkg.pkgScript, VAR_NAME )

# comment any / all of these out to test!
setInstallerVaribale( pkg.pkgScript, VAR_NAME, QT_IFW_HOME_DIR )
#setInstallerVaribale( pkg.pkgScript, VAR_NAME,
# QtIfwControlScript.toBool( True ) )
#setInstallerVaribale( pkg.pkgScript, VAR_NAME,
# QtIfwControlScript.toBool( False ) )

pkg.pkgScript.externalOps += [
setBoolCascadeOp( BOOL_FILE_NAME ), # comment this out to test!
getBoolCascadeOp( BOOL_FILE_NAME,
joinPath( QT_IFW_DESKTOP_DIR, BOOL_EVAL_FILE_NAME ) ),
setBoolCascadeOp( STATIC_BOOL_FILE_NAME ), # comment this out to test!
getBoolCascadeOp( STATIC_BOOL_FILE_NAME,
joinPath( QT_IFW_DESKTOP_DIR, STATIC_BOOL_EVAL_FILE_NAME ) ),

setBoolCascadeOp( DYNAMIC_BOOL_FILE_NAME, VAR_NAME ),
getBoolCascadeOp( DYNAMIC_BOOL_FILE_NAME,
joinPath( QT_IFW_DESKTOP_DIR, DYNAMIC_BOOL_EVAL_FILE_NAME ) ),

setVaribaleCascadeOp( VAR_FILE_NAME, VAR_NAME ),
getVaribaleCascadeOp( VAR_FILE_NAME,
joinPath( QT_IFW_DESKTOP_DIR, VAR_FETCH_FILE_NAME ) ),
Expand Down

0 comments on commit 9a4101e

Please sign in to comment.