diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e5715d5..43df933 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -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/checkout@v4.2.2 - name: build run: | @@ -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/checkout@v4.2.2 - name: build run: | @@ -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 @@ -174,12 +178,16 @@ jobs: - uses: actions/checkout@v4.2.2 - 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/upload-artifact@v4.5.0 if: always() with: @@ -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 @@ -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/upload-artifact@v4.5.0 if: always() with: diff --git a/meson.build b/meson.build index 7840a67..f1e2e31 100644 --- a/meson.build +++ b/meson.build @@ -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'] @@ -38,7 +39,7 @@ funcs = { 'strtonum': '#include ', 'warnx': '#include ', } -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', @@ -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 diff --git a/src/dictpw.nsi b/src/dictpw.nsi index d953404..d2c43f4 100644 --- a/src/dictpw.nsi +++ b/src/dictpw.nsi @@ -1,6 +1,6 @@ #!/usr/bin/makensis -# Copyright (c) 2022 Guilherme Janczak +# Copyright (c) 2022, 2024 Guilherme Janczak # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -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 @@ -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 diff --git a/src/dictpw_installer.sh b/src/dictpw_installer.sh deleted file mode 100755 index f19f299..0000000 --- a/src/dictpw_installer.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -evx - -# Copyright (c) 2022, 2024 Guilherme Janczak -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -OUTFILE='-DNULL' -while getopts o: o; do -case "$o" in - o) OUTFILE="-DOUTFILE=../${OPTARG}";; - *) exit 1;; -esac -done - -if ! [ -d 'build' ]; then - meson setup -Dbuildtype=release build -fi -if ! [ -f 'build/dictpw.exe' ]; then - meson compile -C build -fi - -MSYS='-DNULL' -# Distribute msys-2.0.dll for the MSYS build. -if [ "$MSYSTEM" = "MSYS" ]; then - msysdll='build\msys-2.0.dll' - cp /usr/bin/msys-2.0.dll "$msysdll" - msysdll="../$msysdll" - MSYS="-DMSYS=$msysdll" -fi - -# This profane incantation means "if I'm in MSYS or CLANG64, run MINGW64's -# makensis, else run my environment's makensis." -if [ "$MSYSTEM" = "MSYS" ] || [ "$MSYSTEM" = "CLANG64" ]; then - subsh="/msys2_shell.cmd -defterm -here -no-start -mingw64 -c" -else - subsh='bash -c' -fi - -# The odd quoting is because the whole command line needs to be passed as a -# single argument to the subshell. -$subsh "makensis '$MSYS' '$OUTFILE' src/dictpw.nsi" diff --git a/subprojects/libobsd.wrap b/subprojects/libobsd.wrap index 13d4887..a519412 100644 --- a/subprojects/libobsd.wrap +++ b/subprojects/libobsd.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://github.com/guijan/libobsd -revision = 09bbc61e0e178ae8bf9fbf382c5800e0efdd5165 +revision = 1a6153692545c6bbd1dd634e05988126b510235e depth = 1 [provide]