crackNum 3.31 → 4.0
raw patch · 6 files changed
+164/−27 lines, 6 files
Files
- CHANGES.md +35/−2
- README.md +33/−3
- crackNum.cabal +3/−3
- src/CrackNum/GUI.hs +76/−13
- src/CrackNum/Main.hs +6/−1
- src/CrackNum/TestSuite.hs +11/−5
CHANGES.md view
@@ -1,9 +1,42 @@ * Hackage: <http://hackage.haskell.org/package/crackNum> * GitHub: <http://github.com/LeventErkok/crackNum/> -* Latest Hackage released version: 3.30, 2026-08-21+* Latest Hackage released version: 4.0, 2026-08-24 -### Version 3.31, Not yet released+### Version 4.0, 2026-08-24++ * The Windows GUI now carries the CrackNum icon instead of the stock WinForms+ one. The artwork is rendered by the same generator that produces the macOS+ AppIcon.icns, so the two platforms cannot drift apart.++ * Note on numbering: 3.32 was tagged and published as a GitHub release, but never+ uploaded to Hackage. So for anyone installing from Hackage, this is the first+ version to support Windows -- see the 3.32 entry below for what that involves.++### Version 3.32, 2026-08-24++ * Windows is now a supported platform, with a prebuilt bundle+ (`crackNum-<version>-windows-x86_64.zip`) alongside the Mac and Linux ones. It+ carries the CLI, a native GUI, and a copy of z3, and needs nothing installed:+ the GUI targets .NET Framework 4.8, which ships with Windows 10 and 11, and z3+ brings its own Microsoft C runtime. The binaries are not code-signed, so+ SmartScreen warns on first run; the bundled README says how to get past it.++ * The Windows GUI (`GUI/winGUI/`) is a native WinForms application rather than a+ port of the Tcl/Tk script that serves Linux. Reusing the Tcl would have been a+ far smaller change, but it would have required users to install a Tcl+ distribution before `--gui` did anything, and a GUI behind a prerequisite that+ most people will not clear is worth less than the code it saves.++ * `--version` and the usage text no longer print the executable's `.exe`+ extension on Windows, where `getProgName` keeps it.++ * The golden test suite writes its temporary files with newline translation+ turned off, so the golds stay LF everywhere. Previously, running the suite on+ Windows compared CRLF output against LF golds, and `--accept` there would have+ rewritten every gold in the process.++### Version 3.31, 2026-08-22 * The Hackage description now shows a screenshot of the GUI, and points at the releases page for the prebuilt Mac and Linux bundles. The screenshot was dropped
README.md view
@@ -24,6 +24,7 @@ | --- | --- | --- | | Linux (x86_64) | `crackNum-<version>-linux-x86_64.tar.gz` | Statically linked, so there is no glibc or distribution requirement: it runs as-is on any x86_64 Linux, old or new. | | macOS (Apple Silicon) | `crackNum-<version>-macos-arm64.tar.gz` | Includes `CrackNum.app`. Ad-hoc signed rather than notarized, so clear the quarantine flag as shown below. |+| Windows (x86_64) | `crackNum-<version>-windows-x86_64.zip` | Includes `CrackNumGUI.exe`. Unsigned, so SmartScreen warns on first run; see below. | Unpack the bundle anywhere you like, then put the files somewhere on your `PATH`. `z3` has to go there too, since crackNum shells out to it for every operation.@@ -53,9 +54,24 @@ $ export PATH=$HOME/bin:$PATH # put this in your login shell's startup file ``` -Either way, check with:+**Windows** — keep the files together in one folder; `crackNum.exe` looks beside+itself for the GUI and the solver before consulting your `PATH`: ```+> Expand-Archive crackNum-<version>-windows-x86_64.zip -DestinationPath .+> cd crackNum-<version>-windows-x86_64+> New-Item -ItemType Directory -Force $env:USERPROFILE\bin+> Copy-Item * $env:USERPROFILE\bin+```++Then add that folder to your `PATH` and open a new terminal, since a `PATH` change+does not reach terminals that are already running. The binaries are not code-signed,+so the first run may raise a SmartScreen prompt; choose "More info" then "Run+anyway", or tick "Unblock" in the .zip's Properties before extracting.++Any of the three, check with:++``` $ crackNum -fsp 3.5 $ crackNum --gui ```@@ -411,7 +427,7 @@ the same formats. If you installed from a [release bundle](#prebuilt-binaries-nothing-to-build-no-haskell-toolchain)-the GUI is already in it, and there is nothing to build on either platform. The rest+the GUI is already in it, and there is nothing to build on any of the three. The rest of this section is for installing from Hackage or from a source checkout. **macOS** — a native Swift/AppKit app (`GUI/swiftGUI/`). It is not part of the@@ -437,7 +453,21 @@ script, either put it on your PATH as `crackNum.tcl`, or point at it directly with `CRACKNUM_TCL=/path/to/crackNum.tcl`. -On both platforms, launch the GUI from the command line via the `--gui` option,+**Windows** — a native WinForms app (`GUI/winGUI/`). It targets .NET Framework 4.8,+which ships as part of Windows 10 and 11, so the built executable needs no runtime+install. Like the macOS app it is not part of the Hackage package; building it+needs a clone and the .NET SDK:++```+> git clone https://github.com/LeventErkok/crackNum.git+> cd crackNum\GUI\winGUI+> dotnet build -c Release+```++Put the resulting `CrackNumGUI.exe` next to `crackNum.exe`, or point at it with+`CRACKNUM_GUI=C:\path\to\CrackNumGUI.exe`.++On all three platforms, launch the GUI from the command line via the `--gui` option, which forwards any format/rounding flags and value to the app: ```
crackNum.cabal view
@@ -1,15 +1,15 @@ Cabal-version : 2.2 Name : crackNum-Version : 3.31+Version : 4.0 Synopsis : Crack various integer and floating-point data formats Description : Crack IEEE-754 and other float formats and arbitrary sized words and integers, showing the layout.- Along with a command-line interface on any platform, native MacOS and Tcl-based Linux GUIs are available as well:+ Along with a command-line interface on any platform, native MacOS and Windows GUIs and a Tcl-based Linux GUI are available as well: . <<https://raw.githubusercontent.com/LeventErkok/crackNum/master/crackNumGUI.png>> . For details, please see: <http://github.com/LeventErkok/crackNum/>. See the <https://github.com/LeventErkok/crackNum/releases releases page>- for binary releases for Mac and Linux.+ for binary releases for Mac, Linux, and Windows. License : BSD-3-Clause License-file : LICENSE Author : Levent Erkok
src/CrackNum/GUI.hs view
@@ -19,7 +19,7 @@ import System.Environment (lookupEnv, getExecutablePath) import System.Exit (ExitCode(..)) import System.FilePath (takeDirectory, (</>))-import System.Process (rawSystem)+import System.Process (rawSystem, spawnProcess) import qualified System.Info as Info import Paths_crackNum (getDataFileName)@@ -31,6 +31,26 @@ tclRelPath :: FilePath tclRelPath = "GUI/tclGUI/crackNum.tcl" +-- | The Windows GUI executable. Note the name: Windows file systems are+-- case-insensitive, so calling it CrackNum.exe would make it the very same file as+-- the crackNum.exe it drives, and the two cannot share a directory.+winGUIExe :: FilePath+winGUIExe = "CrackNumGUI.exe"++-- | Where a file would sit if it were next to the running executable.+--+-- NB. getExecutablePath resolves symlinks, so this finds the file even when the+-- binary is reached through a link from elsewhere on the PATH.+besideExe :: FilePath -> IO FilePath+besideExe f = do exe <- getExecutablePath+ pure (takeDirectory exe </> f)++-- | The first candidate that exists; if none do, die with the given message.+search :: [FilePath] -> [String] -> IO FilePath+search [] onFail = die onFail+search (c:cs) onFail = do ok <- doesFileExist c+ if ok then pure c else search cs onFail+ -- | Locate the Tcl/Tk GUI script. Normally it is installed together with the -- binary, so this just works; we look in four places, in order: --@@ -50,22 +70,13 @@ , "" , " " ++ p ]- Nothing -> do beside <- besideExe+ Nothing -> do beside <- besideExe "crackNum.tcl" installed <- getDataFileName tclRelPath mbPath <- findExecutable "crackNum.tcl" case mbPath of Just p -> pure p Nothing -> search [beside, installed] (noTcl beside installed)- where -- NB. getExecutablePath resolves symlinks, so this finds the script even- -- when the binary is reached through a link from elsewhere on the PATH.- besideExe = do exe <- getExecutablePath- pure (takeDirectory exe </> "crackNum.tcl")-- search [] onFail = die onFail- search (c:cs) onFail = do ok <- doesFileExist c- if ok then pure c else search cs onFail-- noTcl beside installed =+ where noTcl beside installed = [ "Cannot find the CrackNum GUI script (crackNum.tcl)." , "" , "Looked in:"@@ -87,13 +98,58 @@ , " git clone http://github.com/LeventErkok/crackNum.git" ] +-- | Locate the Windows GUI executable, mirroring 'locateTcl'. We look in three+-- places, in order:+--+-- 1. $CRACKNUM_GUI, if set: an explicit override, as on macOS.+-- 2. Next to the executable itself, which is how the binary zip is laid out.+-- 3. The PATH, for an installation that puts the two elsewhere.+locateWinGUI :: IO FilePath+locateWinGUI = do mbEnv <- lookupEnv "CRACKNUM_GUI"+ case mbEnv of+ Just p -> do ok <- doesFileExist p+ if ok+ then pure p+ else die [ "The CRACKNUM_GUI environment variable is set, but does not name a file:"+ , ""+ , " " ++ p+ ]+ Nothing -> do beside <- besideExe winGUIExe+ ok <- doesFileExist beside+ if ok+ then pure beside+ else do mbPath <- findExecutable winGUIExe+ case mbPath of+ Just p -> pure p+ Nothing -> die (noGUI beside)+ where noGUI beside =+ [ "Cannot find the CrackNum GUI (" ++ winGUIExe ++ ")."+ , ""+ , "Looked in:"+ , " %CRACKNUM_GUI% (not set)"+ , " " ++ beside+ , " " ++ winGUIExe ++ " on your PATH (not found)"+ , ""+ , "The GUI ships in the same zip as crackNum.exe, so seeing this means the"+ , "copy next to the binary is missing or the binary has been moved."+ , ""+ , "Get the Windows bundle from:"+ , ""+ , " https://github.com/LeventErkok/crackNum/releases"+ , ""+ , "Or point at a copy directly:"+ , ""+ , " $env:CRACKNUM_GUI = \"C:\\path\\to\\" ++ winGUIExe ++ "\""+ ]+ -- | Launch the graphical interface, forwarding all remaining arguments -- (format flags, rounding mode, and/or the value to crack) so the GUI can preselect -- them. The GUI itself calls back into this executable to do the actual cracking. -- -- On macOS the GUI is a Swift/AppKit app; CRACKNUM_GUI can override the .app bundle -- location. On Linux the GUI is a Tcl/Tk script; 'wish' is located via PATH, and--- 'crackNum.tcl' via 'locateTcl'.+-- 'crackNum.tcl' via 'locateTcl'. On Windows it is a WinForms app located by+-- 'locateWinGUI', with CRACKNUM_GUI overriding as it does on macOS. launchGUI :: [String] -> IO () launchGUI vals | Info.os == "darwin"@@ -134,6 +190,13 @@ , "" , "Tried: " ++ wish ++ " " ++ tcl ]+ | Info.os == "mingw32"+ = do gui <- locateWinGUI+ -- Spawned rather than run through rawSystem: the GUI outlives this process,+ -- matching what 'open -n' gives us on macOS. Waiting would pin the console+ -- for as long as the window stayed open.+ _ <- spawnProcess gui vals+ pure () | True = die [ "The --gui option is not supported on this platform (" ++ Info.os ++ ")." , "Use crackNum directly from the command line."
src/CrackNum/Main.hs view
@@ -21,6 +21,7 @@ import qualified Control.Exception as C import System.Environment (getArgs, getProgName, withArgs)+import System.FilePath (dropExtension) import System.Console.GetOpt (ArgOrder(Permute), getOpt) import System.Exit (exitFailure) @@ -42,7 +43,11 @@ -- | main entry point to crackNum main :: IO () main = do argv <- getArgs- pn <- getProgName++ -- dropExtension: on Windows getProgName keeps the .exe, which would put+ -- "crackNum.exe" in the --version banner and every usage message, where+ -- the other platforms say "crackNum".+ pn <- dropExtension <$> getProgName let rt = "--runTests"
src/CrackNum/TestSuite.hs view
@@ -22,6 +22,7 @@ import System.FilePath import System.Directory (removeFile)+import System.IO (IOMode(WriteMode), hPutStr, hSetNewlineMode, noNewlineTranslation, withFile) import System.Process (readProcessWithExitCode) import Data.List (intercalate)@@ -35,12 +36,17 @@ args = words as + -- NB. Written with newline translation off, so the temp file uses LF on+ -- every platform. The golds are LF; plain writeFile would emit CRLF on+ -- Windows, and --accept there would rewrite every gold in the process.+ write f s = withFile f WriteMode $ \h -> hSetNewlineMode h noNewlineTranslation >> hPutStr h s+ run = do (ec, so, se) <- readProcessWithExitCode "crackNum" args ""- writeFile gfTmp $ intercalate "\n" $ [ "Arguments: " ++ as- , "Exit code: " ++ show ec- , so- ]- ++ concat [["STDERR:", se] | not (null se)]+ write gfTmp $ intercalate "\n" $ [ "Arguments: " ++ as+ , "Exit code: " ++ show ec+ , so+ ]+ ++ concat [["STDERR:", se] | not (null se)] diff ref new = ["diff", "-w", "-u", ref, new]