commit 5aab41504e05ad1fc9a3a168be169eb27f8148bd Spenser Truex <truex@equwal.com> 2026-09-20 22:29:48 -0700 Windows: an installer for Cygwin, with a desktop shortcut contrib/windows/build-installer.sh makes sbm-VERSION-setup.exe with NSIS. The installer needs Cygwin. It puts bm and the bm-* tools in /usr/local/bin, and fzf.exe in /usr/local/libexec/sbm. It makes a desktop shortcut that opens bm in mintty, with the key Ctrl+Alt+Shift+F24. The fzf package of Cygwin is version 0.11 from 2016. It has no --preview and no --expect. The wrapper contrib/windows/fzf runs fzf.exe in mintty. /etc/profile.d/sbm.sh sets the opener, the clipboard and --no-height for Cygwin. make dist now includes contrib.
.gitignore | 1 + Makefile | 2 +- README | 21 ++++++ contrib/windows/build-installer.sh | 54 ++++++++++++++ contrib/windows/fzf | 23 ++++++ contrib/windows/sbm-setup | 10 +++ contrib/windows/sbm.nsi | 146 +++++++++++++++++++++++++++++++++++++ contrib/windows/sbm.sh | 11 +++ 8 files changed, 267 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore index d2c33fd..192f813 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ tags.* bm bmks +sbm-*-setup.exe diff --git a/Makefile b/Makefile index 42449fb..1ff7f7c 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ check: dist: @echo creating dist tarball @mkdir -p sbm-${VERSION}-temp - @cp -R Makefile config.mk README TODO ${SCRIPTS} usertags engines test sbm-${VERSION}-temp + @cp -R Makefile config.mk README TODO ${SCRIPTS} usertags engines test contrib sbm-${VERSION}-temp @mv sbm-${VERSION}-temp sbm-${VERSION} @tar -cf sbm-${VERSION}.tar sbm-${VERSION} @gzip sbm-${VERSION}.tar diff --git a/README b/README index 2b0d7fe..8a8ddc0 100644 --- a/README +++ b/README @@ -211,6 +211,27 @@ choices on stdin as one of: Printing nothing means the user cancelled. test/fakemenu is a small example. +== Windows == +bm runs under Cygwin. Get sbm-VERSION-setup.exe from a release on GitHub and +start it. The installer puts bm and the bm-* tools in /usr/local/bin. It +makes a desktop shortcut that opens bm in a mintty window, and Esc closes +the window. The key of the shortcut is Ctrl+Alt+Shift+F24. Almost no keyboard +has an F24 key, so keyboard firmware can use this key combination without +conflicts. + +The installer also puts fzf.exe, the Windows build of fzf, in +/usr/local/libexec/sbm. Do not use the fzf package of Cygwin. That package is +version 0.11 from 2016, and it has no --preview and no --expect. The wrapper +/usr/local/bin/fzf lets fzf.exe run in mintty. /etc/profile.d/sbm.sh sets +SBM_OPEN, SBM_COPY, SBM_PASTE and SBM_FZF_OPTS for Cygwin. Settings in +~/.bash_profile win. + +For bm-title, bm-check and bm-import, install the curl and jq packages of +Cygwin. The Windows builds of curl and jq do not understand Cygwin paths, +such as /dev/null. To remove sbm, use Apps in the Windows settings. The +uninstaller keeps your bookmarks. contrib/windows/build-installer.sh makes +the installer with NSIS. + == Tests == make check diff --git a/contrib/windows/build-installer.sh b/contrib/windows/build-installer.sh new file mode 100755 index 0000000..38db287 --- /dev/null +++ b/contrib/windows/build-installer.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +# build-installer.sh: make sbm-VERSION-setup.exe, the Windows installer, in +# the top directory of the source. The script needs makensis (NSIS), curl, +# unzip and sha256sum. It runs on Linux, and on Windows under Cygwin. +# +# sh contrib/windows/build-installer.sh +# +# The script gets fzf.exe from the fzf release on GitHub. It stops if the +# SHA-256 sum of the download is not the expected one. + +set -e + +fzf_version=0.74.4 +fzf_sum=5e63c0e798406fcb9c51a9fed4988398e25fdabf8c670e32c16caf7b4a7ed02d + +here=$(cd "$(dirname "$0")" && pwd) +top=$(cd "$here/../.." && pwd) +version=$(sed -n 's/^VERSION=//p' "$top/config.mk") +# VIProductVersion needs four numbers: 0.3 becomes 0.3.0.0. +viversion=$(printf '%s.0.0.0\n' "$version" | cut -d. -f1-4) +out=$top/sbm-$version-setup.exe + +# makensis for Windows needs Windows paths. +native () { + if command -v cygpath >/dev/null 2>&1; then + cygpath -w "$1" + else + printf '%s\n' "$1" + fi +} + +work=${TMPDIR:-/tmp}/sbm-installer.$$ +mkdir "$work" +trap 'rm -rf "$work"' EXIT INT TERM +mkdir "$work/libexec" "$work/share" "$work/etc" + +make -s -C "$top" install PREFIX="$work" >/dev/null +cp "$here/fzf" "$work/bin/" +cp "$here/sbm-setup" "$work/libexec/" +cp "$here/sbm.sh" "$work/etc/" +cp "$top/engines" "$top/usertags" "$work/share/" + +url=https://github.com/junegunn/fzf/releases/download/v$fzf_version +curl -fsSL -o "$work/fzf.zip" "$url/fzf-$fzf_version-windows_amd64.zip" +printf '%s %s\n' "$fzf_sum" "$work/fzf.zip" | sha256sum -c - >/dev/null +unzip -q "$work/fzf.zip" fzf.exe -d "$work/libexec" +curl -fsSL -o "$work/libexec/fzf-LICENSE.txt" \ + "https://raw.githubusercontent.com/junegunn/fzf/v$fzf_version/LICENSE" + +makensis -V2 -DVERSION="$version" -DVIVERSION="$viversion" \ + -DSRC="$(native "$work")" -DOUTFILE="$(native "$out")" \ + "$(native "$here/sbm.nsi")" +printf '%s\n' "$out" diff --git a/contrib/windows/fzf b/contrib/windows/fzf new file mode 100755 index 0000000..7893b74 --- /dev/null +++ b/contrib/windows/fzf @@ -0,0 +1,23 @@ +#!/bin/sh + +# fzf: run fzf.exe, the Windows build of fzf, under Cygwin. +# +# The fzf package of Cygwin is version 0.11 from 2016. That version has no +# --preview and no --expect, and bm uses both. The installer puts fzf.exe +# in ../libexec/sbm. Without the installer, this script uses the fzf.exe in +# PATH, for example from "winget install junegunn.fzf". +# +# MSYS=enable_pcon: when MSYS does not contain enable_pcon, fzf.exe in mintty +# starts itself again through a proxy for MSYS2. Cygwin does not need the +# proxy, because Cygwin gives native programs a pseudo console. Cygwin +# ignores MSYS. +# +# SHELL: fzf.exe runs --preview and --bind commands with $SHELL. When SHELL +# is not set, fzf.exe uses cmd.exe, and the bm preview fails. + +exe=${0%/*}/../libexec/sbm/fzf.exe +[ -x "$exe" ] || exe=fzf.exe +MSYS=${MSYS:+$MSYS }enable_pcon +SHELL=${SHELL:-/bin/sh} +export MSYS SHELL +exec "$exe" "$@" diff --git a/contrib/windows/sbm-setup b/contrib/windows/sbm-setup new file mode 100755 index 0000000..b33dead --- /dev/null +++ b/contrib/windows/sbm-setup @@ -0,0 +1,10 @@ +#!/bin/sh + +# sbm-setup: give the user the example tags and search keywords of sbm. +# The installer runs this script. It keeps the files that the user has. + +d=${XDG_DATA_HOME:-$HOME/.local/share}/sbm +mkdir -p "$d" +for f in engines usertags; do + [ -e "$d/$f" ] || cp "/usr/local/share/sbm/$f" "$d/" +done diff --git a/contrib/windows/sbm.nsi b/contrib/windows/sbm.nsi new file mode 100644 index 0000000..30b201c --- /dev/null +++ b/contrib/windows/sbm.nsi @@ -0,0 +1,146 @@ +; sbm.nsi: the Windows installer of sbm. contrib/windows/build-installer.sh +; runs makensis on this file. +; +; The installer needs Cygwin with mintty. It puts bm and the bm-* tools in +; /usr/local/bin, fzf.exe in /usr/local/libexec/sbm, and the settings in +; /etc/profile.d/sbm.sh. It also makes a desktop shortcut that opens bm in a +; mintty window. The key of the shortcut is Ctrl+Alt+Shift+F24. Almost no +; keyboard has an F24 key, so keyboard firmware can use this key combination +; without conflicts. +; +; The uninstaller removes these files. It keeps the bookmarks of the user. + +Unicode true +ManifestDPIAware true +!include LogicLib.nsh + +!ifndef VERSION + !error "Give the version, for example -DVERSION=0.3" +!endif +!ifndef VIVERSION + !error "Give the version in four numbers, for example -DVIVERSION=0.3.0.0" +!endif +!ifndef SRC + !error "Give the directory with the files to install, with -DSRC=dir" +!endif +!ifndef OUTFILE + !error "Give the name of the installer, with -DOUTFILE=file" +!endif + +!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\sbm" +!define MINTTYARGS "--class sbm -t sbm -s 120,32 -p center -o ConfirmExit=no -o Scrollbar=none /bin/bash -lc bm" + +Name "sbm ${VERSION}" +OutFile "${OUTFILE}" +RequestExecutionLevel admin +ShowInstDetails show +ShowUninstDetails show + +VIProductVersion "${VIVERSION}" +VIAddVersionKey "ProductName" "sbm" +VIAddVersionKey "ProductVersion" "${VERSION}" +VIAddVersionKey "FileVersion" "${VERSION}" +VIAddVersionKey "FileDescription" "sbm ${VERSION} installer" +VIAddVersionKey "LegalCopyright" "Spenser Truex" + +Var Cygwin + +Page instfiles +UninstPage uninstConfirm +UninstPage instfiles + +Function .onInit + ; Cygwin setup is a 64-bit program. It writes to the 64-bit registry. + SetRegView 64 + ReadRegStr $Cygwin HKLM "Software\Cygwin\setup" "rootdir" + ${If} $Cygwin == "" + ReadRegStr $Cygwin HKCU "Software\Cygwin\setup" "rootdir" + ${EndIf} + ${IfNot} ${FileExists} "$Cygwin\bin\mintty.exe" + MessageBox MB_ICONSTOP "sbm needs Cygwin with mintty.$\r$\nInstall Cygwin from https://cygwin.com, then start this installer again." + Abort + ${EndIf} +FunctionEnd + +Section + SetOutPath "$Cygwin\usr\local\bin" + File "${SRC}\bin\bm" + File "${SRC}\bin\bm-check" + File "${SRC}\bin\bm-commit" + File "${SRC}\bin\bm-html" + File "${SRC}\bin\bm-import" + File "${SRC}\bin\bm-migrate" + File "${SRC}\bin\bm-title" + File "${SRC}\bin\fzf" + SetOutPath "$Cygwin\usr\local\libexec\sbm" + File "${SRC}\libexec\fzf.exe" + File "${SRC}\libexec\fzf-LICENSE.txt" + File "${SRC}\libexec\sbm-setup" + SetOutPath "$Cygwin\usr\local\share\sbm" + File "${SRC}\share\engines" + File "${SRC}\share\usertags" + SetOutPath "$Cygwin\etc\profile.d" + File "${SRC}\etc\sbm.sh" + + ; Cygwin does not see an execute permission on files that a Windows + ; program makes. Cygwin chmod sets it. + nsExec::ExecToLog '"$Cygwin\bin\chmod.exe" 755 /usr/local/bin/bm /usr/local/bin/bm-check /usr/local/bin/bm-commit /usr/local/bin/bm-html /usr/local/bin/bm-import /usr/local/bin/bm-migrate /usr/local/bin/bm-title /usr/local/bin/fzf /usr/local/libexec/sbm/fzf.exe' + Pop $0 + ${If} $0 != 0 + Abort "chmod failed with exit status $0" + ${EndIf} + + nsExec::ExecToLog '"$Cygwin\bin\sh.exe" /usr/local/libexec/sbm/sbm-setup' + Pop $0 + ${If} $0 != 0 + Abort "sbm-setup failed with exit status $0" + ${EndIf} + + WriteUninstaller "$Cygwin\usr\local\libexec\sbm\uninstall.exe" + + SetShellVarContext current + CreateShortcut /NoWorkingDir "$DESKTOP\sbm.lnk" "$Cygwin\bin\mintty.exe" \ + "${MINTTYARGS}" "$Cygwin\bin\mintty.exe" 0 SW_SHOWNORMAL \ + ALT|CONTROL|SHIFT|F24 "sbm: bookmarks in fzf" + + WriteRegStr HKLM "${UNINSTKEY}" "DisplayName" "sbm" + WriteRegStr HKLM "${UNINSTKEY}" "DisplayVersion" "${VERSION}" + WriteRegStr HKLM "${UNINSTKEY}" "Publisher" "Spenser Truex" + WriteRegStr HKLM "${UNINSTKEY}" "URLInfoAbout" "https://github.com/equwal/sbm" + WriteRegStr HKLM "${UNINSTKEY}" "InstallLocation" "$Cygwin" + WriteRegStr HKLM "${UNINSTKEY}" "UninstallString" '"$Cygwin\usr\local\libexec\sbm\uninstall.exe"' + WriteRegDWORD HKLM "${UNINSTKEY}" "NoModify" 1 + WriteRegDWORD HKLM "${UNINSTKEY}" "NoRepair" 1 +SectionEnd + +Function un.onInit + SetRegView 64 + ReadRegStr $Cygwin HKLM "${UNINSTKEY}" "InstallLocation" + ${IfNot} ${FileExists} "$Cygwin\bin\mintty.exe" + MessageBox MB_ICONSTOP "The registry does not give the Cygwin directory of sbm." + Abort + ${EndIf} +FunctionEnd + +Section Uninstall + Delete "$Cygwin\usr\local\bin\bm" + Delete "$Cygwin\usr\local\bin\bm-check" + Delete "$Cygwin\usr\local\bin\bm-commit" + Delete "$Cygwin\usr\local\bin\bm-html" + Delete "$Cygwin\usr\local\bin\bm-import" + Delete "$Cygwin\usr\local\bin\bm-migrate" + Delete "$Cygwin\usr\local\bin\bm-title" + Delete "$Cygwin\usr\local\bin\fzf" + Delete "$Cygwin\usr\local\libexec\sbm\fzf.exe" + Delete "$Cygwin\usr\local\libexec\sbm\fzf-LICENSE.txt" + Delete "$Cygwin\usr\local\libexec\sbm\sbm-setup" + Delete "$Cygwin\usr\local\libexec\sbm\uninstall.exe" + RMDir "$Cygwin\usr\local\libexec\sbm" + Delete "$Cygwin\usr\local\share\sbm\engines" + Delete "$Cygwin\usr\local\share\sbm\usertags" + RMDir "$Cygwin\usr\local\share\sbm" + Delete "$Cygwin\etc\profile.d\sbm.sh" + SetShellVarContext current + Delete "$DESKTOP\sbm.lnk" + DeleteRegKey HKLM "${UNINSTKEY}" +SectionEnd diff --git a/contrib/windows/sbm.sh b/contrib/windows/sbm.sh new file mode 100644 index 0000000..010ed9c --- /dev/null +++ b/contrib/windows/sbm.sh @@ -0,0 +1,11 @@ +# sbm.sh: settings for bm under Cygwin. The installer puts this file in +# /etc/profile.d, and each login shell reads it. The shell reads +# ~/.bash_profile and ~/.profile later, so settings there win. + +# Open URLs with the default browser of Windows. +export SBM_OPEN="${SBM_OPEN:-cygstart}" +# Use the Windows clipboard. sed removes the CR at the end of each line. +export SBM_COPY="${SBM_COPY:-cp /dev/stdin /dev/clipboard}" +export SBM_PASTE="${SBM_PASTE:-sed s/\r\$// /dev/clipboard}" +# fzf.exe reads non-ASCII input only in full screen. +export SBM_FZF_OPTS="${SBM_FZF_OPTS:---no-height}"