Spec: M1 — the zixl command builds installer packages #1
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jeff/zixl#1
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem Statement
A Zig project that ships a Windows program wants to hand its users an
.msiinstaller package, but building one today means either the WiX Toolset — a .NET program at home on Windows — or GNOME msitools'wixl, the reference implementation, which stands on glib, libgsf, libgcab, libxml2 and libmsi and does not build easily on Windows. Neither fits a project built with Zig and Nix on Linux CI runners, and neither produces the same bytes twice, so a Nix-built installer package is never reproducible. wixl and libmsi are also LGPL, so nothing derived from their code can become an MIT-licensed dependency of an ordinarybuild.zig.Solution
zixl, a command and a Zig library that turn WiX v3 sources into installer packages on Linux or Windows, written in Zig 0.17 with no C libraries (ADR-0002). It is a clean-room reimplementation of what the reference implementation does, written from the public specifications and licensed MIT (ADR-0001). Its command line takes the same options as the reference implementation wherever zixl implements the feature behind them, so a script switches over by changing one word. Its output is reproducible: the same WiX sources and source paths always give the same bytes (ADR-0003).This spec covers milestone 1: the
zixlcommand builds a real per-machine installer package for a command-line program, for x86, x64 or arm64, which installs under Windows Installer.User Stories
Building an installer package
zixl -o app.msi app.wxs, so that I get an installer package from a WiX source without installing the WiX Toolset or msitools.zixlto build installer packages, so that my CI runners never need Windows.zixlto run natively, so that I can build the installer package locally the way CI does.SOURCE_DATE_EPOCHwhen it is set, so that reproducible builds do not record the build time.-osays, and named the way the reference implementation names it when-ois left out, so that zixl behaves the way the reference implementation does.-o,-D,-a/--arch,-I/--includedir,-E,-vand--versionto mean what they mean to wixl, so that my scripts work after changing one word.--ext,--extdiror--wxidir, to stop with an error saying it is not supported, so that I am never handed an installer package that silently lacks something.Architectures
-a x64to produce a 64-bit installer package, so that my program installs underProgram Filesrather thanProgram Files (x86).-a x86and-a arm64as well, so that I can ship every Windows architecture from one WiX source.$(sys.BUILDARCH)to name the architecture being built, so that one WiX source can choose directories and conditions per architecture.Preprocessing
<?define?>and<?undef?>, so that I can name values once and use them throughout.-D NAME=VALUEon the command line to define a variable, so that CI can pass the version number in.$(var.NAME),$(env.NAME)and$(sys.CURRENTDIR),$(sys.SOURCEFILEDIR)and$(sys.SOURCEFILEPATH), so that sources can refer to definitions, the environment and their own location.<?if?>,<?ifdef?>,<?ifndef?>,<?else?>,<?elseif?>and<?endif?>, so that one source can vary by architecture or configuration.<?include?>to pull in.wxifiles, searched for relative to the including file and then in each-Idirectory, so that shared fragments live in one place.<?foreach?>, so that repetitive elements can be generated from a list.<?warning?>and<?error?>, so that a source can refuse a configuration it does not support.zixl -Eto print the preprocessed source and stop, so that I can see what my conditionals and includes produced.WiX elements
ProductandPackageto set the product name, version, manufacturer, language, codes and package summary information, so that Windows shows my installer correctly.DirectoryandDirectoryRef, including the standard folders such asProgramFiles64Folder, so that I can lay out where things are installed.Component,ComponentRef,ComponentGroupandComponentGroupRef, so that I can group what is installed together and reference groups from features.File, with its source path, so that my program's files are put into the installer package.FeatureandFeatureRef, so that components are attached to what the user installs.Fragment, so that parts of the installer can live in separate WiX sources and be referenced.Property, so that I can set Windows Installer properties.Icon, so that my product shows an icon in Add/Remove Programs and on shortcuts.Shortcut, so that my program appears in the Start menu.RegistryKeyandRegistryValue, so that my program can record its install location or register itself.Environment, so that my command-line program's directory can be added toPATH.CreateFolderandRemoveFolder, so that empty directories are created and cleaned up.MajorUpgrade, so that installing a new version replaces the old one.Conditionas a launch condition, so that the installer refuses to run where my program cannot.Media, withCabinet,EmbedCabandDiskId, so that I control how files are packed.Identity and GUIDs
Id="*"onProductandPackageandGuid="*"onComponentto have zixl generate the GUID, so that I do not have to make them up.Idgiven to two elements of the same kind to be an error naming both locations, so that I find the clash quickly.Files and source paths
\separators to work, so that a WiX source written on Windows builds unchanged.CompressionLevel="none"storing them uncompressed, so that I can trade size for speed..cabfiles beside it, asEmbedCabsays, so that both distribution styles work.Mediaelements with differentDiskIds, so that files can be split across cabinets.Codepages and text
CodepageandSummaryCodepageto choose the codepage strings are stored in, defaulting to 1252, so that non-English product names display correctly.Diagnostics
Idthat does not exist to be an error naming the missingId, so that broken references never reach the installer package.-vto report what zixl is doing, so that I can follow a build that surprises me.Library
zixlmodule, so that I can build installer packages without running a program.Io.Writer, so that I choose where the installer package goes.Project
Implementation Decisions
zixlmodule, named as inGLOSSARY.md:Io.Writer, plus any external cabinets to writers the caller provides.The command line is a thin layer over these. Nothing it does is unavailable to a library user.
std.Io, tests implement it in memory, and the M2build.zigstep will implement it overLazyPaths. Source paths reach it with\and/already normalized and relative to the WiX source's directory; case is never folded.includes that led there. The first error stops linking and writing, but compiling collects as many as it can before stopping.SOURCE_DATE_EPOCHsupplies timestamps when set. UUIDs come fromzig-uuid, ported to Zig 0.17 in a session of its own before linking is begun.zxml. Text is encoded into the database codepage withzig-charset.std.compress.flate. LZX is not implemented.x64;1033,Intel;1033,Arm64;1033and so on), the minimum installer version, and the 64-bit component attribute.wixl --helpexactly for the options it implements. The executable is namedzixl, with nowixlalias.Testing Decisions
zig build test): most tests live here. A test gives the library WiX source text, defines, an architecture and an in-memory file source, and asserts on the resulting installer database's tables, rows and summary information, or on the diagnostics produced. Every element, attribute default and error case in the user stories gets a test here.zig build test): WiX source text and defines in, preprocessed text or diagnostics out, the same outputzixl -Eprints. Every directive, variable kind and error case gets a test.zixlcommand end to end (NixOS VM tests,nix flake check, onnixos-do-m): for each fixture WiX source,msidump, and diff the tables with generated columns (GUIDs, timestamps) masked;msiinfoopens zixl's output;msiexec /iand check the files, shortcuts, registry values and environment changes landed;This is the only seam that sees bytes, so the writing stage is tested here alone until the M3 reader exists. Every fixture is built for x86, x64 and arm64 where the reference implementation supports the architecture.
use_llvm.Out of Scope
build.zigstep (M2).msiinfo- ormsidump-like tools (M3).--extextension.wixl-heat.Further Notes
zig-uuidsupporting Zig 0.17. Preprocessing and compiling up to GUID derivation can proceed without it.GLOSSARY.mdanddocs/adr/0001–0003are the vocabulary and decisions this spec relies on.