Skip to content

Commit

Permalink
make the installer a Meson target
Browse files Browse the repository at this point in the history
Set the minimum Meson version to 1.4.0 because we use the filesystem
module's copy_file.
Remove the obsolete dictpw_installer.sh script.

Some issues with the installer were exposed by the change and fixed:

Fix mixture of tabs/spaces in the script, use only spaces.
Reorder uninstall operations for more robustness.
  • Loading branch information
guijan committed Dec 29, 2024
1 parent b0ed915 commit 415b6ee
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 170 deletions.
21 changes: 14 additions & 7 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ jobs:
steps:
- name: setup
run: |
pip install meson
sudo apt update
sudo apt install -y dietlibc-dev meson valgrind linux-headers-generic
sudo apt install -y dietlibc-dev linux-headers-generic ninja-build \
valgrind
- uses: actions/[email protected]
- name: build
run: |
Expand Down Expand Up @@ -84,8 +86,9 @@ jobs:
steps:
- name: setup
run: |
pip install meson
sudo apt update
sudo apt install -y meson
sudo apt install -y ninja-build
- uses: actions/[email protected]
- name: build
run: |
Expand Down Expand Up @@ -165,6 +168,7 @@ jobs:
path-type: strict
pacboy: |
gcc:p meson:p ninja:p dos2unix: git: groff:
# Some environments have no nsis, so we use mingw-w64's.
- name: env-lacks-nsis
if: matrix.sys == 'msys' || matrix.sys == 'clang64'
run: pacman -S --noconfirm --needed mingw-w64-x86_64-nsis
Expand All @@ -174,12 +178,16 @@ jobs:
- uses: actions/[email protected]
- name: build
run: |
if [ "$MSYSTEM" = "MSYS" ] || [ "$MSYSTEM" = "CLANG64" ]; then
PATH="${PATH}:/mingw64/bin"
fi
meson setup build
meson compile -C build
- name: test
run: meson test -C build
- name: installer
run: src/dictpw_installer.sh -o 'build\setup-dictpw-${{matrix.sys}}.exe'
run: |
meson compile installer -C build
- uses: actions/[email protected]
if: always()
with:
Expand All @@ -189,7 +197,7 @@ jobs:
if: success()
with:
name: setup-${{matrix.sys}}
path: build/setup-*.exe
path: build/setup-dictpw.exe

netbsd:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -254,9 +262,8 @@ jobs:
run: meson test -C build
- name: inst
run: |
$out = "..\build\setup-dictpw-vs-${{matrix.cc}}-${{matrix.arch}}.exe"
makensis -DOUTFILE="$out" src/dictpw.nsi
ls build/
meson compile installer -C build
ls -R build/
- uses: actions/[email protected]
if: always()
with:
Expand Down
68 changes: 52 additions & 16 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

project('dictpw', 'c', version: '1.2.0', license: 'ISC',
default_options: ['c_std=c99', 'warning_level=3'])
default_options: ['c_std=c99', 'warning_level=3'],
meson_version: '>=1.4.0')

# Makes all functions, including BSD functions, visible on GNU.
args = ['-D_GNU_SOURCE']
Expand All @@ -38,7 +39,7 @@ funcs = {
'strtonum': '#include <stdlib.h>',
'warnx': '#include <err.h>',
}
libbsd_dep = []
libbsd_dep = dependency('', required: false)
foreach func, header : funcs
if not cc.has_function(func, prefix: header, args: args)
libbsd_dep = dependency('libbsd-overlay',
Expand Down Expand Up @@ -103,18 +104,53 @@ if host_machine.system() == 'windows' or host_machine.system() == 'cygwin'

# Put copies of the license and the README with DOS newlines in the build
# directory too.
custom_target('license',
command: [unix2dos],
input: ['LICENSE.md'],
feed: true,
output: ['LICENSE.txt'],
capture: true,
build_by_default: true)
custom_target('readme',
command: [unix2dos],
input: ['README.md'],
feed: true,
output: ['README.txt'],
capture: true,
build_by_default: true)
license = custom_target('license',
command: [unix2dos],
input: ['LICENSE.md'],
feed: true,
output: ['LICENSE.txt'],
capture: true,
build_by_default: true)
readme = custom_target('readme',
command: [unix2dos],
input: ['README.md'],
feed: true,
output: ['README.txt'],
capture: true,
build_by_default: true)

fs = import('fs')
inst_cmd = [find_program('makensis'), '-NOCD',
'-INPUTCHARSET', 'UTF8', '-OUTPUTCHARSET', 'UTF8',
'-XSetCompressor /SOLID /FINAL lzma',
'-DMESON=true',
'-DOUTFILE=setup-dictpw.exe',
'-DEXEFILE=' + fs.name(dictpw.full_path()),
'-DDOCFILE=' + fs.name(man.full_path()),
'-DLICENSE=' + fs.name(license.full_path()),
'-DREADME=' + fs.name(readme.full_path())]
if libbsd_dep.found() and libbsd_dep.type_name() == 'internal'
inst_cmd += '-DLIBOBSD_LICENSE=true'
endif
# Programs built in Cygwin and MSYS2's MSYS environment are linked against
# a special DLL with their implementations of Unix inside, distribute it.
dll_copy = []
if host_machine.system() == 'cygwin'
dlls = ['/usr/bin/msys-2.0.dll', '/bin/cygwin1.dll']
found = false
foreach dll : dlls
if fs.is_file(dll)
dll_copy += fs.copyfile(dll)
inst_cmd += '-DMSYS_DLL=' + fs.name(dll)
found = true
break
endif
endforeach
if not found
error('cygwin/msys2 DLL not found')
endif
endif
run_target('installer',
command: inst_cmd + ['--', files('src/dictpw.nsi')],
depends: [dictpw, man, license, readme, dll_copy])
endif
189 changes: 94 additions & 95 deletions src/dictpw.nsi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/makensis

# Copyright (c) 2022 Guilherme Janczak <[email protected]>
# Copyright (c) 2022, 2024 Guilherme Janczak <[email protected]>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
Expand All @@ -14,28 +14,37 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

# This script isn't meant to be used directly. dictpw_installer.sh is its
# interface.

!include "FileFunc.nsh"

!ifndef MESON
!error "This NSIS installer is intended to be built by Meson"
!endif
!ifndef OUTFILE
!error "Output file unspecified"
!endif
!ifndef EXEFILE
!define EXEFILE "..\build\dictpw.exe"
!error "dictpw.exe unspecified"
!endif

!ifndef DOCFILE
!define DOCFILE "..\build\dictpw.txt"
!error "dictpw.txt unspecified"
!endif

!ifndef OUTFILE
!define OUTFILE "..\build\setup-dictpw.exe"
!ifndef LICENSE
!error "LICENSE.txt unspecified"
!endif

!define LIBOBSD_LICENSE "..\build\subprojects\libobsd\LICENSE_libobsd.txt"
!ifndef README
!error "README.txt unspecified"
!endif
# LIBOBSD_LICENSE is optional, it may not be needed on msys2.
# Meson can't pass files from subprojects, so we get it ourselves.
!ifdef LIBOBSD_LICENSE
!undef LIBOBSD_LICENSE
!define LIBOBSD_LICENSE "subprojects\libobsd\LICENSE_libobsd.txt"
!endif
# MSYS_DLL is optional, only needed on msys2 and Cygwin.

!define MUI_DIRECTORYPAGE_VARIABLE "$INSTDIR"
!include "MUI2.nsh"
!insertmacro MUI_PAGE_LICENSE "..\LICENSE.md"
!insertmacro MUI_PAGE_LICENSE "${LICENSE}"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_DIRECTORY
Expand All @@ -48,100 +57,90 @@ RequestExecutionLevel admin
showinstdetails show

Section
# Uninstalling previous versions allows changing the installed files
# without leaving any lingering files.
Call UninstPrevious

SetOutPath $INSTDIR
CreateDirectory "$INSTDIR\bin"
File /oname=bin\dictpw.exe "${EXEFILE}"
# Read: If we're building for the MSYS2 environment, distribute the msys DLL.
!ifdef MSYS
File /oname=bin\msys-2.0.dll "${MSYS}"
# Uninstalling previous versions allows changing the installed files
# without leaving any lingering files.
Call UninstPrevious

SetOutPath "$INSTDIR\bin"
File "${EXEFILE}"
!ifdef MSYS_DLL
File "${MSYS_DLL}"
!endif
File "..\build\LICENSE.txt"
!if /FileExists "${LIBOBSD_LICENSE}"
File "${LIBOBSD_LICENSE}"

SetOutPath "$INSTDIR"
File "${LICENSE}" "${README}" "${DOCFILE}"
!ifdef LIBOBSD_LICENSE
File "${LIBOBSD_LICENSE}"
!endif
File "..\build\README.txt"
File "${DOCFILE}"
WriteUninstaller "$INSTDIR\uninstall.exe"

# Add/Remove programs registry
!define UN "Software\Microsoft\Windows\CurrentVersion\Uninstall\dictpw"
WriteRegStr HKLM "${UN}" \
"InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKLM "${UN}" \
"DisplayName" \
"dictpw -- generate password from dictionary"
WriteRegStr HKLM "${UN}" \
"UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "${UN}" \
"QuietUninstallString" \
"$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "${UN}" \
"URLUpdateInfo" "https://github.com/guijan/dictpw"
WriteRegStr HKLM "${UN}" \
"URLInfoAbout" "https://github.com/guijan/dictpw"
WriteRegDWORD HKLM "${UN}" \
"NoModify" 0x00000001
WriteRegDWORD HKLM "${UN}" \
"NoRepair" 0x00000001

# Windows needs to be told the install size manually.
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKLM "${UN}" "EstimatedSize" "$0"

# Allows running the program with `start /b /wait dictpw`
!define AP \
"Software\Microsoft\Windows\CurrentVersion\App Paths\dictpw.exe"
WriteRegStr HKLM "${AP}" "" "$INSTDIR\bin\dictpw.exe"
# Read: If we're building for the MSYS2 or Cygwin environments, distribute their
# DLLs.
WriteUninstaller "$INSTDIR\uninstall.exe"

# Add/Remove programs registry
!define UN "Software\Microsoft\Windows\CurrentVersion\Uninstall\dictpw"
WriteRegStr HKLM "${UN}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKLM "${UN}" "DisplayName" \
"dictpw -- generate password from dictionary"
WriteRegStr HKLM "${UN}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "${UN}" "QuietUninstallString" \
"$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKLM "${UN}" "URLUpdateInfo" "https://github.com/guijan/dictpw"
WriteRegStr HKLM "${UN}" "URLInfoAbout" "https://github.com/guijan/dictpw"
WriteRegDWORD HKLM "${UN}" "NoModify" 0x00000001
WriteRegDWORD HKLM "${UN}" "NoRepair" 0x00000001

# Windows needs to be told the install size manually.
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKLM "${UN}" "EstimatedSize" "$0"

# Allows running the program with `start /b /wait dictpw`
!define AP "Software\Microsoft\Windows\CurrentVersion\App Paths\dictpw.exe"
WriteRegStr HKLM "${AP}" "" "$INSTDIR\bin\dictpw.exe"
SectionEnd

Section "Uninstall"
DeleteRegKey HKLM "${AP}"
DeleteRegKey HKLM "${UN}"
Delete "$INSTDIR\dictpw.txt"
Delete "$INSTDIR\README.txt"
!if /FileExists "${LIBOBSD_LICENSE}"
Delete "$INSTDIR\LICENSE_libobsd.txt"
DeleteRegKey HKLM "${AP}"
Delete "$INSTDIR\bin\dictpw.exe"
Delete "$INSTDIR\LICENSE.txt"
Delete "$INSTDIR\README.txt"
Delete "$INSTDIR\dictpw.txt"
!ifdef LIBOBSD_LICENSE
Delete "$INSTDIR\LICENSE_libobsd.txt"
!endif
Delete "$INSTDIR\LICENSE.txt"
!ifdef MSYS
Delete "$INSTDIR\bin\msys-2.0.dll"
!ifdef MSYS_DLL
Delete "$INSTDIR\bin\${MSYS_DLL}"
!endif
Delete "$INSTDIR\bin\dictpw.exe"
RMDir "$INSTDIR\bin"
RMDir "$INSTDIR\bin"

# Make sure to delete uninstall.exe only after everything else is
# deleted.
Delete "$INSTDIR\uninstall.exe"
# Delete uninstall.exe only after everything else is deleted.
Delete "$INSTDIR\uninstall.exe"
RMDir "$INSTDIR"

RMDir "$INSTDIR"
# Remove the uninstaller from the registry after the uninstallation is done.
DeleteRegKey HKLM "${UN}"
SectionEnd


Function UninstPrevious
Push $R0
Push $R1

# References:
# https://nsis.sourceforge.io/Talk:Auto-uninstall_old_before_installing_new
# https://stackoverflow.com/questions/719631/how-do-i-require-user-to-uninstall-previous-version-with-nsis
ReadRegStr $R1 HKLM "${UN}" "InstallLocation"
StrCmp $R1 "" ret
ReadRegStr $R0 HKLM "${UN}" "QuietUninstallString"

# Remove the first and the last characters of InstallLocation, that is,
# its enclosing quotes.
# This is for:
# https://nsis.sourceforge.io/When_I_use_ExecWait_uninstaller.exe_it_doesn%27t_wait_for_the_uninstaller
StrCpy $R1 $R1 "" 1
StrCpy $R1 $R1 -1
ExecWait "$R0 _?=$R1"
Push $R0
Push $R1

# References:
# https://nsis.sourceforge.io/Talk:Auto-uninstall_old_before_installing_new
# https://stackoverflow.com/questions/719631/how-do-i-require-user-to-uninstall-previous-version-with-nsis
ReadRegStr $R1 HKLM "${UN}" "InstallLocation"
StrCmp $R1 "" ret
ReadRegStr $R0 HKLM "${UN}" "QuietUninstallString"

# Remove the first and the last characters of InstallLocation, that is,
# its enclosing quotes.
# This is for:
# https://nsis.sourceforge.io/When_I_use_ExecWait_uninstaller.exe_it_doesn%27t_wait_for_the_uninstaller
StrCpy $R1 $R1 "" 1
StrCpy $R1 $R1 -1
ExecWait "$R0 _?=$R1"

ret:
Pop $R1
Pop $R0
Pop $R1
Pop $R0
FunctionEnd
Loading

0 comments on commit 415b6ee

Please sign in to comment.