Something went wrong. Try again.
Builds Windows Installer packages from WiX sources on Linux or Windows, in pure Zig; a clean-room reimplementation of msitools' wixl.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! The standard Windows Installer tables an installer package is given, with//! their columns as the Windows Installer SDK documents them.//!//! Every installer package gets all of them, whether or not it has rows for//! them, which is what the reference implementation does.
const std = @import("std");
const Database = @import("Database.zig");const Column = Database.Column;
pub const Table = struct { name: []const u8, columns: []const Column,};
/// Columns written as `Name:definition`, separated by spaces, with a leading/// `*` on each primary key column. Definitions are in Windows Installer's/// column definition format, as `Column.parse` reads it.fn table(comptime name: []const u8, comptime spec: []const u8) Table { @setEvalBranchQuota(100_000); comptime var columns: []const Column = &.{}; comptime var it = std.mem.tokenizeScalar(u8, spec, ' '); inline while (comptime it.next()) |item| { const key = item[0] == '*'; const rest = if (key) item[1..] else item; const colon = comptime std.mem.findScalar(u8, rest, ':').?; columns = columns ++ [_]Column{Column.parse(rest[0..colon], rest[colon + 1 ..], key)}; } const final = columns[0..columns.len].*; return .{ .name = name, .columns = &final };}
pub const standard = [_]Table{ table("ActionText", "*Action:s72 Description:L255 Template:L255"), table("AdminExecuteSequence", "*Action:s72 Condition:S255 Sequence:I2"), table("AdminUISequence", "*Action:s72 Condition:S255 Sequence:I2"), table("AdvtExecuteSequence", "*Action:s72 Condition:S255 Sequence:I2"), table("AppSearch", "*Property:s72 *Signature_:s72"), table("Binary", "*Name:s72 Data:v0"), table("Component", "*Component:s72 ComponentId:S38 Directory_:s72 Attributes:i2 Condition:S255 KeyPath:S72"), table("Condition", "*Feature_:s38 *Level:i2 Condition:S255"), table("CreateFolder", "*Directory_:s72 *Component_:s72"), table("CustomAction", "*Action:s72 Type:i2 Source:S72 Target:S255 ExtendedType:I4"), table("Directory", "*Directory:s72 Directory_Parent:S72 DefaultDir:l255"), table("Environment", "*Environment:s72 Name:l64 Value:L255 Component_:s72"), table("Error", "*Error:i2 Message:L0"), table("Feature", "*Feature:s38 Feature_Parent:S38 Title:L64 Description:L255 Display:I2 Level:i2 Directory_:S72 Attributes:i2"), table("FeatureComponents", "*Feature_:s38 *Component_:s72"), table("File", "*File:s72 Component_:s72 FileName:l255 FileSize:i4 Version:S72 Language:S20 Attributes:I2 Sequence:i4"), table("Icon", "*Name:s72 Data:v0"), table("IniFile", "*IniFile:s72 FileName:l255 DirProperty:S72 Section:l255 Key:l255 Value:l255 Action:i2 Component_:s72"), table("InstallExecuteSequence", "*Action:s72 Condition:S255 Sequence:I2"), table("InstallUISequence", "*Action:s72 Condition:S255 Sequence:I2"), table("LaunchCondition", "*Condition:s255 Description:l255"), table("Media", "*DiskId:i2 LastSequence:i4 DiskPrompt:L64 Cabinet:S255 VolumeLabel:S32 Source:S72"), table("MsiFileHash", "*File_:s72 Options:i2 HashPart1:i4 HashPart2:i4 HashPart3:i4 HashPart4:i4"), table("Property", "*Property:s72 Value:l0"), table("RegLocator", "*Signature_:s72 Root:i2 Key:s255 Name:S255 Type:I2"), table("Registry", "*Registry:s72 Root:i2 Key:l255 Name:L255 Value:L0 Component_:s72"), table("RemoveFile", "*FileKey:s72 Component_:s72 FileName:L255 DirProperty:s72 InstallMode:i2"), table("RemoveIniFile", "*RemoveIniFile:s72 FileName:l255 DirProperty:S72 Section:l255 Key:l255 Value:L255 Action:i2 Component_:s72"), table("ServiceControl", "*ServiceControl:s72 Name:l255 Event:i2 Arguments:L255 Wait:I2 Component_:s72"), table("ServiceInstall", "*ServiceInstall:s72 Name:s255 DisplayName:L255 ServiceType:i4 StartType:i4 ErrorControl:i4 LoadOrderGroup:S255 Dependencies:S255 StartName:S255 Password:S255 Arguments:S255 Component_:s72 Description:L255"), table("Shortcut", "*Shortcut:s72 Directory_:s72 Name:l128 Component_:s72 Target:s72 Arguments:S255 Description:L255 Hotkey:I2 Icon_:S72 IconIndex:I2 ShowCmd:I2 WkDir:S72 DisplayResourceDLL:S255 DisplayResourceId:I2 DescriptionResourceDLL:S255 DescriptionResourceId:I2"), table("Signature", "*Signature:s72 FileName:s255 MinVersion:S20 MaxVersion:S20 MinSize:I4 MaxSize:I4 MinDate:I4 MaxDate:I4 Languages:S255"), table("Upgrade", "*UpgradeCode:s38 *VersionMin:S20 *VersionMax:S20 *Language:S255 *Attributes:i4 Remove:S255 ActionProperty:s72"),};
/// The standard table called `name`.pub fn find(name: []const u8) ?Table { for (standard) |t| { if (std.mem.eql(u8, t.name, name)) return t; } return null;}
test "every standard table has a primary key first" { for (standard) |t| { try std.testing.expect(t.columns.len > 0); try std.testing.expect(t.columns[0].key); }}
test "Feature" { const t = find("Feature").?; try std.testing.expectEqual(@as(usize, 8), t.columns.len); try std.testing.expectEqualStrings("Feature_Parent", t.columns[1].name); try std.testing.expect(t.columns[1].nullable); try std.testing.expect(t.columns[2].localizable);}