Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Report installation messages to console #847

Merged
merged 22 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions constructor/nsis/main.nsi.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,26 @@ Unicode true
!endif
!macroend

var /global QuietMode # "0" = print normally, "1" = do not print
var /global StdOutHandle
var /global StdOutHandleSet
!define Print "!insertmacro PrintMacro"
!macro PrintMacro INPUT_TEXT
DetailPrint "${INPUT_TEXT}"
${If} ${Silent}
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::Call 'kernel32::AttachConsole(i -1)i.r1'
${If} $0 = 0
${OrIf} $1 = 0
System::Call 'kernel32::AllocConsole()'
${AndIf} $QuietMode != "1"
${IfNot} $StdOutHandleSet == "1"
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
FileWrite $0 "${INPUT_TEXT}$\n"
System::Call 'kernel32::AttachConsole(i -1)i.r1'
${If} $0 = 0
${OrIf} $1 = 0
System::Call 'kernel32::AllocConsole()'
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
${EndIf}
StrCpy $StdOutHandle $0
StrCpy $StdOutHandleSet "1"
${EndIf}
${EndIf}
!macroend

!define CloseStdout "!insertmacro CloseStdout"
!macro CloseStdout
${If} ${Silent}
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
FileClose $0
System::Free $0
System::Free $1
System::Call 'kernel32::FreeConsole()'
FileWrite $StdOutHandle "${INPUT_TEXT}$\n"
${EndIf}
!macroend

Expand Down Expand Up @@ -112,6 +109,7 @@ var /global ARGV_NoRegistry
var /global ARGV_NoScripts
var /global ARGV_NoShortcuts
var /global ARGV_CheckPathLength
var /global ARGV_QuietMode

var /global IsDomainUser
var /global CheckPathLength
Expand Down Expand Up @@ -284,6 +282,7 @@ FunctionEnd
/CheckPathLength=[0|1] [default: 1]$\n\
/? (show this help message)$\n\
/S (run in CLI/headless mode)$\n\
/Q (omit CLI logging)$\n\
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
/D=[installation directory] (must be last parameter)$\n"
# There seems to be a limit to how many chars per ${Print} we can pass.
# The message will get truncated silently, no errors.
Expand Down Expand Up @@ -376,6 +375,12 @@ FunctionEnd
${EndIf}
${EndIf}

ClearErrors
${GetOptions} $ARGV "/Q" $ARGV_QuietMode
${IfNot} ${Errors}
StrCpy $QuietMode "1"
${EndIf}

!macroend

Function OnInit_Release
Expand Down Expand Up @@ -1339,7 +1344,6 @@ Section "Install"
AccessControl::SetOnFile "$INSTDIR" "(DU)" "GenericRead + GenericExecute"
${EndIf}
${Print} "Done!"
${CloseStdout}
SectionEnd

!macro AbortRetryNSExecWaitLibNsisCmd cmd
Expand Down
2 changes: 2 additions & 0 deletions constructor/winexe.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ def make_nsi(
ppd["custom_welcome"] = info.get("welcome_file", "").endswith(".nsi")
ppd["custom_conclusion"] = info.get("conclusion_file", "").endswith(".nsi")
ppd["has_license"] = bool(info.get("license_file"))
ppd["post_install_pages"] = bool(info.get("post_install_pages"))

data = preprocess(data, ppd)
data = fill_template(data, replace, exceptions=nsis_predefines)
if info['_platform'].startswith("win") and sys.platform != 'win32':
Expand Down
31 changes: 28 additions & 3 deletions docs/source/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ Windows installers have the following CLI options available:
`0`.
- `/RegisterPython=[0|1]`: Whether to register Python as default in the Windows registry. Defaults
to `1`. This is preferred to `AddToPath`.
- `/Q` (quiet): do not report to the console in headless mode. Only relevant when used with `/S`
(see below).

You can also supply [standard NSIS flags](https://nsis.sourceforge.io/Docs/Chapter3.html#installerusage), but only _after_ the ones mentioned above:

- `/NCRC`: disables the CRC check.
- `/S` (silent): runs the installer or uninstaller in headless mode. Installers created with
`constructor 3.10` or later will report information to the active console. Note that this
is not the stdout stream of the process, so stream redirection (like `>`, `>>` or `|`)
won't work.
`constructor 3.10` or later will report information to the active console.
- `/D` (directory): sets the default installation directory. Note that even if the path contains
spaces, it must be the last parameter used in the command line and must not contain any quotes.
Only absolute paths are supported. The uninstaller uses `_?` instead of `/D`.
Expand Down Expand Up @@ -104,6 +104,31 @@ Run the uninstaller in silent mode from its original location:
> cmd.exe /c start /wait "C:\Program Files\my_app\uninstaller.exe" /S _?=C:\Program Files\my_app
```

:::{admonition} Stream redirection in EXE installers
:class: info

When run in `/S` mode, EXE installers will output some minimal info to the console when invoked
like this:

```batch
> cmd.exe /c start /wait my_installer.exe /S
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmd.exe /c is not necessary. You could also consider adding PowerShell since this is the default profile in the Windows Terminal

Start-Process -FilePath my_installer.exe -ArgumentList "/S" -NoNewWindow -Wait

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, you can even use -RedirectStandardOutput out.txt to correctly redirect things!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this to the documentation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you replaced the documentation. I don't see the harm showing both cmd.exe and PowerShell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tabs with both now.

```

This call waits until the installer is done. However, stream redirection won't work like this:

```batch
> cmd.exe /c start /wait my_installer.exe /S > logs.txt
```

Stream redirection does work as expected if run directly:

```batch
> my_installer.exe /S > logs.txt
```

Unfortunately, in this case the call won't block. You will have to poll the `logs.txt` file and block until finished (e.g. you find `Done!` or `:error:`).
:::

:::{admonition} EXE installers with file logging
:class: tip

Expand Down
2 changes: 1 addition & 1 deletion docs/source/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ See `man installer` for more details.

## Verbose EXE installers

Windows installers do not have a verbose mode. By default, the graphical logs are only available in the "progress bar" dialog, by clicking on "Show details". This text box is right-clickable, which will allow you to copy the contents to the clipboard (and then paste them in a text file, presumably).
By default, the graphical logs are only available in the "progress bar" dialog, by clicking on "Show details". This text box is right-clickable, which will allow you to copy the contents to the clipboard (and then paste them in a text file, presumably).

If you want `conda` to print more details, then, run it from the CMD prompt like this:

Expand Down
Loading