Skip to content

Add Windows deployment pipeline (icon, installer, GUI subsystem)#2261

Merged
cxxxr merged 1 commit into
mainfrom
feature/windows-deployment
Jul 3, 2026
Merged

Add Windows deployment pipeline (icon, installer, GUI subsystem)#2261
cxxxr merged 1 commit into
mainfrom
feature/windows-deployment

Conversation

@cxxxr

@cxxxr cxxxr commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Package the webview frontend as a normal Windows application: lem.exe with the Lem icon and version info, no console window, all required DLLs bundled, plus a portable zip and an Inno Setup installer.

Running scripts\win-deploy.ps1 produces:

  • bin\lem.exe + DLLs (webview.dll, OpenSSL, libwinpthread) — deploy-op output, GUI subsystem, icon/version resources embedded
  • build\windows\Lem-<version>-windows-x64.zip — portable archive
  • build\windows\Lem-<version>-setup.exe — Inno Setup installer (Start menu entry, uninstaller, optional desktop icon / PATH / "Open with Lem" Explorer context menu, English and Japanese UI, per-user or per-machine install)

How it works

  • Icon: PE resources cannot be edited on the finished executable — rewriting them corrupts the Lisp core that save-lisp-and-die appends ("Can't find sbcl.core"). The script instead embeds icon and version info into a copy of the SBCL runtime with rcedit, then dumps the image through that runtime.
  • Build: uses the deploy library via the existing :build-operation "deploy-op" in lem.asd ((asdf:make :lem)), which bundles the foreign libraries loaded in the image into bin/ and loads them from the executable's directory at startup. qlot is bypassed (it does not compile on Windows SBCL); dependencies come from plain Quicklisp. rcedit and ASDF 3.3.7 are downloaded pinned and sha256-verified into build\windows\.
  • GUI subsystem: deploy builds Windows executables with :application-type :gui, but a console-less process has invalid stdio handles and the first write kills the writing thread (the known reason the earlier manual :gui build broke). src/windows.lisp (Windows counterpart of macosx.lisp) fixes this with an sb-ext:*init-hooks* hook that redirects the low-level stdio streams to %TEMP%\lem-stdio.log when no console is present — it must be an init hook because deploy's warm boot writes status output before any deploy :boot hook runs. It also tells deploy not to bundle OS-provided DLLs (kernel32 from async-process, winhttp) or the optional tree-sitter libraries.

Test plan

  • scripts\win-deploy.ps1 builds bin\lem.exe (Windows 11, SBCL 2.6.4) and both packages
  • Icon and version info visible on the executable (ProductName=Lem, FileVersion=2.3.0.0)
  • Launch from Explorer (no console): window opens, editor stays alive, stdio redirected to log
  • Launch from a console parent: works (deploy attaches the parent console)
  • bin\ contains only application DLLs (no kernel32.dll/winhttp.dll)

🤖 Generated with Claude Code

Package the webview frontend as a normal Windows application:

- scripts/win-deploy.ps1: one-shot build/package script. Embeds the
  application icon and version info into a copy of the SBCL runtime
  with rcedit before dumping (editing PE resources of the finished
  executable corrupts the appended Lisp core), builds bin/ via the
  deploy library, then produces a portable zip and an Inno Setup
  installer. rcedit and ASDF 3.3.7 are downloaded pinned and
  sha256-verified; qlot is bypassed because it does not work on
  Windows.
- scripts/win-deploy.lisp: build script loaded by the patched runtime;
  quickloads :lem and runs (asdf:make :lem) (deploy-op).
- scripts/install/lem.iss: Inno Setup script with Start menu entry,
  uninstaller, optional desktop icon / PATH / "Open with Lem" context
  menu, English and Japanese UI, per-user or per-machine install.
- src/windows.lisp: Windows counterpart of macosx.lisp. Redirects the
  invalid stdio handles of a console-less GUI-subsystem process to a
  log file via an SBCL init hook (deploy's warm boot writes status
  output before :boot hooks run, so a deploy hook would be too late),
  and tells deploy not to bundle OS-provided DLLs (kernel32, winhttp)
  or optional tree-sitter libraries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@code-contractor-app

code-contractor-app Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

✅ Code Contractor Validation: PASSED

=== Contract: contract ===

✓ Code Contractor Validation Result
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📋 Contract Source: Repository

📊 Statistics:
  Files Changed:    2
  Lines Added:      55
  Lines Deleted:    1
  Total Changed:    56
  Delete Ratio:     0.02 (2%)

Status: PASSED ✅

🤖 AI Providers:
  - codex — model: (Codex default)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 No violations detected. Great job!
📋 Contract Configuration: contract (Source: Repository)
version: 2

trigger:
  paths:
    - "extensions/**"
    - "frontends/**/*.lisp"
    - "src/**"
    - "tests/**"
    - "contrib/**"
    - "**/*.asd"
  head_branches:
    exclude:
      - 'revert-*'

validation:
  limits:
    max_total_changed_lines: 400
    max_delete_ratio: 0.5
    max_files_changed: 10
    severity: warning

  ai:
    system_prompt: |
      You are a senior Common Lisp engineer reviewing code for Lem editor.
      Lem is a text editor with multiple frontends (ncurses, SDL2, webview).
      Focus on maintainability, consistency with existing code, and Lem-specific conventions.
    rules:
      # === File Structure ===
      - name: defpackage_rule
        prompt: |
          First form must be `defpackage` or `uiop:define-package`.
          Package name should match filename (e.g., `foo.lisp` → `:lem-ext/foo` or `:lem-foo`).
          Extensions must use `lem-` prefix (e.g., `:lem-python-mode`).

      - name: file_structure_rule
        prompt: |
          File organization (top to bottom):
          1. defpackage
          2. defvar/defparameter declarations
          3. Key bindings (define-key, define-keys)
          4. Class/struct definitions
          5. Functions and commands

      # === Style ===
      - name: loop_keywords_rule
        prompt: |
          Loop keywords must use colons: `(loop :for x :in list :do ...)`
          NOT: `(loop for x in list do ...)`

      - name: naming_conventions_rule
        prompt: |
          Naming conventions:
          - Functions/variables: kebab-case (e.g., `find-buffer`)
          - Special variables: *earmuffs* (e.g., `*global-keymap*`)
          - Constants: +plus-signs+ (e.g., `+default-tab-size+`)
          - Predicates: -p suffix for functions (e.g., `buffer-modified-p`)
          - Do NOT use -p suffix for user-configurable variables

      # === Documentation ===
      - name: docstring_rule
        prompt: |
          Required docstrings for:
          - Exported functions, methods, classes
          - `define-command` (explain what the command does)
          - Generic functions (`:documentation` option)
          Important functions should explain "why", not just "what".
        severity: warning

      # === Lem-Specific ===
      - name: internal_symbol_rule
        prompt: |
          Use exported symbols from `lem` or `lem-core` package.
          Avoid `lem::internal-symbol` access.
          If internal access is necessary, document why.

      - name: error_handling_rule
        prompt: |
          - `error`: Internal/programming errors
          - `editor-error`: User-facing errors (displayed in echo area)
          Always use `editor-error` for messages shown to users.

      - name: frontend_interface_rule
        prompt: |
          Frontend-specific code must use `lem-if:*` protocol.
          Do not call frontend implementation directly from core.
        severity: warning

      # === Functional Style ===
      - name: functional_style_rule
        prompt: |
          Prefer explicit function arguments over dynamic variables.
          Avoid using `defvar` for state passed between functions.
          Exception: Well-documented cases like `*current-buffer*`.

      - name: dynamic_symbol_call_rule
        prompt: |
          Avoid `uiop:symbol-call`. Rethink architecture instead.
          If unavoidable, document the reason.

      # === Libraries ===
      - name: alexandria_usage_rule
        prompt: |
          Alexandria utilities allowed: `if-let`, `when-let`, `with-gensyms`, etc.
          Avoid: `alexandria:curry` (use explicit lambdas)
          Avoid: `alexandria-2:*` functions not yet used in codebase

      # === Macros ===
      - name: macro_style_rule
        prompt: |
          Keep macros small. For complex logic, use `call-with-*` pattern:
          ```lisp
          (defmacro with-foo (() &body body)
            `(call-with-foo (lambda () ,@body)))
          ```
          Prefer `list` over backquote outside macros.
📚 About Code Contractor

Declarative Code Standards That Learn and Improve

Define domain-specific validation rules in YAML.
Your contracts document team knowledge and evolve into more accurate AI enforcement.

Want this for your repo?
Install Code Contractor

@code-contractor-app code-contractor-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Contractor validation passed ✅ — see the sticky comment for full results.

@cxxxr cxxxr merged commit 6f26c60 into main Jul 3, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant