Skip to content

Commit 5491467

Browse files
committedJan 9, 2013
Merge remote-tracking branch 'wixl/master'
2 parents 06c9626 + b57de21 commit 5491467

28 files changed

+4491
-9
lines changed
 

‎AUTHORS

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
msitools are maintained by Paolo Bonzini. <pbonzini@redhat.com>
1+
msitools are maintained by Paolo Bonzini <pbonzini@redhat.com> and
2+
Marc-Andre Lureau <marcandre.lureau@gmail.com>.
23

34
libmsi is mostly based on Wine's implementation of Windows Installer,
45
with the following copyright:

‎LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Format: http://www.debian.org/doc/copyright-format/1.0
2+
Upstream-Name: Wixl
3+
4+
Files: *
5+
Copyright: Copyright 2013 Red Hat, Inc.
6+
License: LGPL-2+
7+
This program is free software; you can redistribute it and/or modify
8+
it under the terms of the GNU Lesser General Public License as published by
9+
the Free Software Foundation; either version 2 of the License, or
10+
(at your option) any later version.
11+
.
12+
This program is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
GNU Lesser General Public License for more details.
16+
.
17+
You should have received a copy of the GNU Lesser General Public License
18+
along with this program; if not, write to the Free Software Foundation,
19+
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

‎Makefile.am

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
ACLOCAL_AMFLAGS = -I m4
22
SUBDIRS = libmsi tests .
33

4+
if WIXL
5+
SUBDIRS += po wixl
6+
endif
7+
48
dist_include_HEADERS = \
59
include/libmsi.h \
610
include/libmsi-database.h \
@@ -33,7 +37,7 @@ DISTCLEANFILES = atconfig atlocal
3337
CLEANFILES = testsuite.log
3438

3539
check-local: $(srcdir)/tests/testsuite atconfig atlocal
36-
$(SHELL) $(srcdir)/tests/testsuite AUTOTEST_PATH=. $(TESTSUITEFLAGS)
40+
$(SHELL) $(srcdir)/tests/testsuite AUTOTEST_PATH=.:wixl $(TESTSUITEFLAGS)
3741

3842
installcheck-local: $(srcdir)/tests/testsuite atconfig atlocal
3943
$(SHELL) $(srcdir)/tests/testsuite AUTOTEST_PATH=$(bindir) $(TESTSUITEFLAGS)

‎README

+16-6
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,26 @@ implementation of the Windows Installer.
66
msitools plans to be a solution for packaging and deployment of
77
cross-compiled Windows applications.
88

9-
While in a very early stage, it is already usable. Two tools are
10-
provided, msiinfo and msibuild, respectively to inspect and create
11-
MSI files. They are very low-level, but it should already be possible
12-
to use these tools to take an existing .MSI file, and update it with
13-
new build products (for example after a cross-compilation).
9+
Provided tools include:
10+
11+
- msiinfo, to inspect MSI files
12+
13+
- msibuild, a low-level tool to create MSI files
14+
15+
- wixl, a WiX-like tool, that builds Windows Installer (MSI) packages
16+
from an XML document, and tries to share the same syntax as the WiX
17+
toolset, http://wixtoolset.org/
18+
19+
While in a very early stage, it is already usable.
1420

1521
msitools uses libgsf in order to read OLE Structured Storage files
1622
(which are the underlying format of .MSI files). As of December 7th,
1723
2012 you need to build libgsf from git because a required bugfix is not
1824
found in any release. Fedora packages for a fixed libgsf can be found
1925
at http://bonzini.fedorapeople.org/.
2026

21-
For more information, contact me at pbonzini@redhat.com.
27+
Wixl lacks many features compared to WiX. As always, contributions
28+
are welcome!
29+
30+
For more information, please contact us by email. Forking and sending
31+
github pull requests is also welcome.

‎atlocal.in

+4
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ _msiinfo() {
2222
_msibuild() {
2323
WINEDEBUG=-all msibuild$EXEEXT "$@"
2424
}
25+
26+
_wixl() {
27+
wixl$EXEEXT "$@"
28+
}

‎autogen.sh

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/sh
2+
3+
test -n "$srcdir" || srcdir=$(dirname "$0")
4+
test -n "$srcdir" || srcdir=.
5+
(
6+
cd "$srcdir" &&
7+
mkdir -p m4 &&
8+
AUTOPOINT='intltoolize --automake --copy' autoreconf -fiv -Wall
9+
) || exit
10+
test -n "$NOCONFIGURE" || "$srcdir/configure" --enable-maintainer-mode "$@"

‎configure.ac

+36-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ AC_USE_SYSTEM_EXTENSIONS
1515
AC_PROG_CC
1616
AC_PROG_YACC
1717

18+
IT_PROG_INTLTOOL([0.35])
19+
1820
AM_PATH_GLIB_2_0([2.12.0])
1921
PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 gio-2.0 >= 2.14])
2022
PKG_CHECK_MODULES([GSF], [libgsf-1])
@@ -28,11 +30,32 @@ LT_INIT([win32-dll disable-fast-install])
2830
GOBJECT_INTROSPECTION_CHECK([0.9.4])
2931
AM_CONDITIONAL([GIR], [test "x$INTROSPECTION_MAKEFILE" != x])
3032

31-
AM_PROG_VALAC([0.14])
33+
AM_PROG_VALAC([0.16])
3234
AC_PATH_PROG(VAPIGEN, vapigen, no)
3335
AC_SUBST(VAPIGEN)
3436
AM_CONDITIONAL([VAPI], [test "x$VAPIGEN" != xno])
3537

38+
PKG_CHECK_MODULES([WIXL], [gio-2.0 >= 2.26.0
39+
libgcab-1.0
40+
uuid >= 1.41.3
41+
libxml-2.0 >= 2.9],
42+
[wixl_ok=yes], [wixl_ok=no])
43+
44+
AC_ARG_ENABLE([wixl],
45+
[AS_HELP_STRING([--disable-wixl], [do not build wixl (default=yes)])],
46+
[wixl=$enableval], [wixl=yes])
47+
48+
AS_IF([test $wixl_ok = no && test $wixl != no],
49+
[AC_MSG_ERROR([Wixl dependencies not found])])
50+
51+
AM_CONDITIONAL([WIXL], [test "x$wixl" != xno])
52+
53+
GETTEXT_PACKAGE=wixl
54+
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [Gettext Package])
55+
AC_SUBST(GETTEXT_PACKAGE)
56+
AM_GNU_GETTEXT_VERSION([1.11])
57+
AM_GLIB_GNU_GETTEXT
58+
3659
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
3760

3861
AM_MISSING_PROG([AUTOM4TE], [autom4te])
@@ -45,7 +68,19 @@ AC_CONFIG_FILES([tests/package.m4.tmp:tests/package.m4.in],
4568
AC_CONFIG_FILES([
4669
Makefile
4770
libmsi-1.0.pc
71+
po/Makefile.in
4872
libmsi/Makefile
73+
wixl/Makefile
4974
tests/Makefile
5075
])
5176
AC_OUTPUT
77+
78+
AC_MSG_NOTICE([
79+
msitools $VERSION
80+
================
81+
82+
prefix: ${prefix}
83+
c compiler: ${CC}
84+
uuid: ${uuid}
85+
wixl: ${wixl}
86+
])

‎po/LINGUAS

Whitespace-only changes.

‎po/POTFILES.in

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[encoding: UTF-8]
2+
wixl/wixl.vala
3+
wixl/builder.vala

‎po/POTFILES.skip

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
wixl/builder.c
2+
wixl/wixl.c
3+
libmsi/sql-parser.c

‎tests/data/wixl/FoobarAppl10.exe

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
x

‎tests/data/wixl/Helper.dll

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
x

‎tests/data/wixl/Manual.pdf

1 Byte
Binary file not shown.

‎tests/data/wixl/Manual.wxs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version='1.0' encoding='windows-1252'?>
2+
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
3+
<Fragment Id='FragmentManual'>
4+
5+
<DirectoryRef Id='INSTALLDIR'>
6+
<Component Id='Manual' Guid='ABCDDCBA-574D-4A9A-A266-5B5EC2C022A4'>
7+
<File Id='Manual' Name='Manual.pdf' DiskId='1' Source='Manual.pdf' KeyPath='yes'>
8+
<Shortcut Id="startmenuManual" Directory="ProgramMenuDir" Name="Instruction Manual" Advertise="yes" />
9+
</File>
10+
</Component>
11+
</DirectoryRef>
12+
13+
</Fragment>
14+
</Wix>

‎tests/data/wixl/SampleFirst.wxs

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version='1.0' encoding='windows-1252'?>
2+
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
3+
<Product Name='Foobar 1.0' Id='ABCDDCBA-86C7-4D14-AEC0-86416A69ABDE' UpgradeCode='ABCDDCBA-7349-453F-94F6-BCB5110BA4FD'
4+
Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='Acme Ltd.'>
5+
6+
<Package Id='*' Keywords='Installer' Description="Acme's Foobar 1.0 Installer"
7+
Comments='Foobar is a registered trademark of Acme Ltd.' Manufacturer='Acme Ltd.'
8+
InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
9+
10+
<Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
11+
<Property Id='DiskPrompt' Value="Acme's Foobar 1.0 Installation [1]" />
12+
13+
<Directory Id='TARGETDIR' Name='SourceDir'>
14+
<Directory Id='ProgramFilesFolder' Name='PFiles'>
15+
<Directory Id='Acme' Name='Acme'>
16+
<Directory Id='INSTALLDIR' Name='Foobar 1.0'>
17+
18+
<Component Id='MainExecutable' Guid='ABCDDCBA-83F1-4F22-985B-FDB3C8ABD471'>
19+
<File Id='FoobarEXE' Name='FoobarAppl10.exe' DiskId='1' Source='FoobarAppl10.exe' KeyPath='yes'>
20+
<Shortcut Id="startmenuFoobar10" Directory="ProgramMenuDir" Name="Foobar 1.0" WorkingDirectory='INSTALLDIR' Icon="Foobar10.exe" IconIndex="0" Advertise="yes" />
21+
<Shortcut Id="desktopFoobar10" Directory="DesktopFolder" Name="Foobar 1.0" WorkingDirectory='INSTALLDIR' Icon="Foobar10.exe" IconIndex="0" Advertise="yes" />
22+
</File>
23+
</Component>
24+
25+
<Component Id='HelperLibrary' Guid='ABCDDCBA-6BE3-460D-A14F-75658D16550B'>
26+
<File Id='HelperDLL' Name='Helper.dll' DiskId='1' Source='Helper.dll' KeyPath='yes' />
27+
</Component>
28+
29+
<Component Id='Manual' Guid='ABCDDCBA-574D-4A9A-A266-5B5EC2C022A4'>
30+
<File Id='Manual' Name='Manual.pdf' DiskId='1' Source='Manual.pdf' KeyPath='yes'>
31+
<Shortcut Id="startmenuManual" Directory="ProgramMenuDir" Name="Instruction Manual" Advertise="yes" />
32+
</File>
33+
</Component>
34+
35+
</Directory>
36+
</Directory>
37+
</Directory>
38+
39+
<Directory Id="ProgramMenuFolder" Name="Programs">
40+
<Directory Id="ProgramMenuDir" Name="Foobar 1.0">
41+
<Component Id="ProgramMenuDir" Guid="ABCDDCBA-7E98-44CE-B049-C477CC0A2B00">
42+
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
43+
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes' />
44+
</Component>
45+
</Directory>
46+
</Directory>
47+
48+
<Directory Id="DesktopFolder" Name="Desktop" />
49+
</Directory>
50+
51+
<Feature Id='Complete' Level='1'>
52+
<ComponentRef Id='MainExecutable' />
53+
<ComponentRef Id='HelperLibrary' />
54+
<ComponentRef Id='Manual' />
55+
<ComponentRef Id='ProgramMenuDir' />
56+
</Feature>
57+
58+
<Icon Id="Foobar10.exe" SourceFile="FoobarAppl10.exe" />
59+
60+
</Product>
61+
</Wix>

‎tests/data/wixl/SampleFragment.wxs

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version='1.0' encoding='windows-1252'?>
2+
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
3+
<Product Name='Foobar 1.0' Id='ABCDDCBA-86C7-4D14-AEC0-86416A69ABDE' UpgradeCode='ABCDDCBA-7349-453F-94F6-BCB5110BA4FD'
4+
Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='Acme Ltd.'>
5+
6+
<Package Id='*' Keywords='Installer' Description="Acme's Foobar 1.0 Installer"
7+
Comments='Foobar is a registered trademark of Acme Ltd.' Manufacturer='Acme Ltd.'
8+
InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
9+
10+
<Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
11+
<Property Id='DiskPrompt' Value="Acme's Foobar 1.0 Installation [1]" />
12+
13+
<Directory Id='TARGETDIR' Name='SourceDir'>
14+
<Directory Id='ProgramFilesFolder' Name='PFiles'>
15+
<Directory Id='Acme' Name='Acme'>
16+
<Directory Id='INSTALLDIR' Name='Foobar 1.0'>
17+
18+
<Component Id='MainExecutable' Guid='ABCDDCBA-83F1-4F22-985B-FDB3C8ABD471'>
19+
<File Id='FoobarEXE' Name='FoobarAppl10.exe' DiskId='1' Source='FoobarAppl10.exe' KeyPath='yes'>
20+
<Shortcut Id="startmenuFoobar10" Directory="ProgramMenuDir" Name="Foobar 1.0" WorkingDirectory='INSTALLDIR' Icon="Foobar10.exe" IconIndex="0" Advertise="yes" />
21+
<Shortcut Id="desktopFoobar10" Directory="DesktopFolder" Name="Foobar 1.0" WorkingDirectory='INSTALLDIR' Icon="Foobar10.exe" IconIndex="0" Advertise="yes" />
22+
</File>
23+
</Component>
24+
25+
<Component Id='HelperLibrary' Guid='ABCDDCBA-6BE3-460D-A14F-75658D16550B'>
26+
<File Id='HelperDLL' Name='Helper.dll' DiskId='1' Source='Helper.dll' KeyPath='yes' />
27+
</Component>
28+
29+
</Directory>
30+
</Directory>
31+
</Directory>
32+
33+
<Directory Id="ProgramMenuFolder" Name="Programs">
34+
<Directory Id="ProgramMenuDir" Name="Foobar 1.0">
35+
<Component Id="ProgramMenuDir" Guid="ABCDDCBA-7E98-44CE-B049-C477CC0A2B00">
36+
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
37+
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes' />
38+
</Component>
39+
</Directory>
40+
</Directory>
41+
42+
<Directory Id="DesktopFolder" Name="Desktop" />
43+
</Directory>
44+
45+
<Feature Id='Complete' Title='Foobar 1.0' Description='The complete package.'
46+
Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR'>
47+
<Feature Id='MainProgram' Title='Program' Description='The main executable.' Level='1'>
48+
<ComponentRef Id='MainExecutable' />
49+
<ComponentRef Id='HelperLibrary' />
50+
<ComponentRef Id='ProgramMenuDir' />
51+
</Feature>
52+
53+
<Feature Id='Documentation' Title='Description' Description='The instruction manual.' Level='1'>
54+
<ComponentRef Id='Manual' />
55+
</Feature>
56+
</Feature>
57+
58+
<Icon Id="Foobar10.exe" SourceFile="FoobarAppl10.exe" />
59+
60+
</Product>
61+
</Wix>

‎tests/data/wixl/test-arp.wxs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0"?>
2+
<?define Version = "0.2.0"?>
3+
<?define UpgradeCode = "ABCDDCBA-8392-0202-1993-199374829923"?>
4+
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
5+
6+
<Product Id="*" Name="name" Manufacturer="manufacturer"
7+
Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)"
8+
Language="1033">
9+
10+
<Package InstallerVersion="200" Compressed="yes" Comments="comments"/>
11+
<Media Id="1" Cabinet="cabinet.cab" EmbedCab="yes"/>
12+
13+
<Property Id="ARPHELPLINK" Value="http://www.foobar.baz"/>
14+
<Property Id="ARPNOMODIFY" Value="1"/>
15+
<Property Id="ARPNOREPAIR" Value="1"/>
16+
<Property Id="ARPPRODUCTICON" Value="FoobarAppl10.exe"/>
17+
<Property Id="ARPURLINFOABOUT" Value="http://www.foobar.baz/info"/>
18+
<Upgrade Id="$(var.UpgradeCode)">
19+
<UpgradeVersion Minimum="$(var.Version)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED"/>
20+
<UpgradeVersion Minimum="0.0.0" Maximum="$(var.Version)" IncludeMinimum="yes" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED"/>
21+
</Upgrade>
22+
<Condition Message="A newer version is already installed.">NOT NEWERVERSIONDETECTED</Condition>
23+
24+
<Directory Id="TARGETDIR" Name="SourceDir">
25+
<Directory Id="ProgramFilesFolder">
26+
<Directory Id="INSTALLDIR" Name="Example">
27+
<Component Id="MainExecutable" Guid="ABCDDCBA-2034-1019-3233-949940039491">
28+
<File Id="FoobarAppl10.exe" Source="FoobarAppl10.exe"/>
29+
</Component>
30+
</Directory>
31+
</Directory>
32+
</Directory>
33+
34+
<Feature Id="Complete" Level="1">
35+
<ComponentRef Id="MainExecutable"/>
36+
</Feature>
37+
38+
<InstallExecuteSequence>
39+
<RemoveExistingProducts After="InstallValidate"/>
40+
</InstallExecuteSequence>
41+
42+
<Icon Id="FoobarAppl10.exe" SourceFile="FoobarAppl10.exe"/>
43+
44+
</Product>
45+
</Wix>

‎tests/testsuite.at

+2
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,5 @@ Application: Windows Installer XML (3.7.1119.0)
231231
Security: 2 (2)
232232
])
233233
AT_CLEANUP
234+
235+
m4_include([wixl.at])

‎tests/wixl.at

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
AT_BANNER([wixl])
2+
3+
# AT_CHECK_... - add exeext automatically
4+
m4_define([AT_CHECK_WIXL], [
5+
AT_CHECK([_wixl ]$@)])
6+
7+
# AT_WIXLDATA - copy data file from source tree
8+
m4_define([AT_WIXLDATA], [
9+
dir=`dirname $1`
10+
AS_MKDIR_P([$dir])
11+
AT_CHECK([cp $abs_srcdir/tests/data/wixl/$1 $1])])
12+
13+
14+
AT_SETUP([Invalid command line])
15+
AT_CHECK_WIXL([], [1], [ignore], [ignore])
16+
AT_CHECK_WIXL([out.msi foo.wxs], [1], [ignore], [ignore])
17+
AT_CHECK_WIXL([-o out.msi], [1], [ignore], [ignore])
18+
AT_CHECK_WIXL([-E], [1], [ignore], [ignore])
19+
AT_CHECK_WIXL([-D], [1], [ignore], [ignore])
20+
AT_CHECK_WIXL([-E -o out.msi], [1], [ignore], [ignore])
21+
AT_CHECK([test -f out.msi], [1])
22+
AT_CLEANUP
23+
24+
AT_SETUP([WiX tutorial SampleFirst])
25+
AT_WIXLDATA([SampleFirst.wxs])
26+
AT_WIXLDATA([FoobarAppl10.exe])
27+
AT_WIXLDATA([Helper.dll])
28+
AT_WIXLDATA([Manual.pdf])
29+
AT_CHECK_WIXL([-o out.msi SampleFirst.wxs], [0], [ignore], [ignore])
30+
# FIXME: add tons of tests on out.msi
31+
AT_CHECK([test -f out.msi], [0])
32+
AT_CLEANUP
33+
34+
AT_SETUP([WiX tutorial SampleFragment])
35+
AT_WIXLDATA([SampleFragment.wxs])
36+
AT_WIXLDATA([Manual.wxs])
37+
AT_WIXLDATA([FoobarAppl10.exe])
38+
AT_WIXLDATA([Helper.dll])
39+
AT_WIXLDATA([Manual.pdf])
40+
AT_CHECK_WIXL([-o out.msi SampleFragment.wxs Manual.wxs], [0], [ignore], [ignore])
41+
# FIXME: add tons of tests on out.msi
42+
AT_CHECK([test -f out.msi], [0])
43+
AT_CLEANUP
44+
45+
AT_SETUP([Preprocessor variables])
46+
export MY_VAR="Hello!"
47+
AT_DATA([variables.wxs], [<?xml version="1.0"?>
48+
<?define Version = "0.2.0"?>
49+
<?define UpgradeCode = "ABCDDCBA-8392-0202-1993-199374829923"?>
50+
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
51+
<Property Id="Id0" Value="$(var.UpgradeCode)"/>
52+
<Property Id="Id0.1" Value="$$(var.UpgradeCode)"/>
53+
<Property Id="Id0.2" Value="$$$(var.UpgradeCode)"/>
54+
<?define UpgradeCode = "ABCDDCBA-8392-0202-1993-199374829924"?>
55+
<Property Id="Id2" Value="$(var.UpgradeCode)"/>
56+
<Property Id="Id3" Value="$(var.Version)"/>
57+
<?define A = "A"?><?define B = "B"?>
58+
<Property Id="IdAB" Value="$(var.A)$(var.B)"/>
59+
<Property Id="IdHello" Value="$(env.MY_VAR)"/>
60+
<Property Id="IdSys" Value="($(sys.SOURCEFILEDIR))foo"/>
61+
</Wix>
62+
])
63+
AT_CHECK_WIXL([-E variables.wxs], [0], [<?xml version="1.0"?>
64+
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
65+
<Property Id="Id0" Value="ABCDDCBA-8392-0202-1993-199374829923"/>
66+
<Property Id="Id0.1" Value="$ABCDDCBA-8392-0202-1993-199374829923"/>
67+
<Property Id="Id0.2" Value="$$ABCDDCBA-8392-0202-1993-199374829923"/>
68+
<Property Id="Id2" Value="ABCDDCBA-8392-0202-1993-199374829924"/>
69+
<Property Id="Id3" Value="0.2.0"/>
70+
<Property Id="IdAB" Value="AB"/>
71+
<Property Id="IdHello" Value="Hello!"/>
72+
<Property Id="IdSys" Value="(variables.wxs)foo"/>
73+
</Wix>
74+
], [ignore])
75+
AT_DATA([variables.wxs], [<?xml version="1.0"?>
76+
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
77+
<Property Id="Id$(var.Foo)" Value="$(var.Foo)"/>
78+
<Property Id="Id$(var.Zig)" Value="$(var.Zig)"/>
79+
</Wix>
80+
])
81+
AT_CHECK_WIXL([-E variables.wxs -D Foo -D Zig=Zag], [0], [<?xml version="1.0"?>
82+
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
83+
<Property Id="Id1" Value="1"/>
84+
<Property Id="IdZag" Value="Zag"/>
85+
</Wix>
86+
], [ignore])
87+
AT_CLEANUP
88+
89+
AT_SETUP([ARP example])
90+
AT_WIXLDATA([test-arp.wxs])
91+
AT_WIXLDATA([FoobarAppl10.exe])
92+
AT_CHECK_WIXL([-o out.msi test-arp.wxs], [0], [ignore], [ignore])
93+
# FIXME: add tons of tests on out.msi
94+
AT_CHECK([test -f out.msi], [0])
95+
AT_CLEANUP

‎vapi/libxml-2.0.vapi

+1,863
Large diffs are not rendered by default.

‎wixl/Makefile.am

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
NULL =
2+
bin_PROGRAMS = wixl
3+
4+
AM_CFLAGS = -w
5+
6+
# --vapidir paths are relative to the source directory!
7+
8+
AM_VALAFLAGS = \
9+
-H wixl.h --use-header \
10+
--vapidir=. \
11+
--vapidir=../vapi \
12+
--vapidir=$(abs_top_builddir)/libmsi \
13+
--pkg config \
14+
--enable-experimental \
15+
--pkg gio-2.0 \
16+
--pkg libmsi-1.0 \
17+
--pkg libgcab-1.0 \
18+
--pkg libxml-2.0 \
19+
--pkg posix \
20+
$(NULL)
21+
22+
wixl_SOURCES = \
23+
builder.vala \
24+
msi.vala \
25+
preprocessor.vala \
26+
util.vala \
27+
wix.vala \
28+
wixl.vala \
29+
$(NULL)
30+
31+
AM_CPPFLAGS = \
32+
-include config.h \
33+
-I $(top_srcdir)/include \
34+
$(WIXL_CFLAGS) \
35+
-DG_LOG_DOMAIN=\""wixl"\" \
36+
-DLOCALEDIR=\""$(localedir)"\" \
37+
-DPKGDATADIR=\""$(pkgdatadir)"\" \
38+
-DPKGLIBDIR=\""$(pkglibdir)"\" \
39+
$(NULL)
40+
41+
wixl_LDADD = \
42+
$(WIXL_LIBS) \
43+
../libmsi/libmsi.la \
44+
$(NULL)
45+
46+
wixl_DEPENDENCIES = ../libmsi/libmsi.la

‎wixl/builder.vala

+590
Large diffs are not rendered by default.

‎wixl/config.vapi

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[CCode (prefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
2+
namespace Config
3+
{
4+
public const string PACKAGE_NAME;
5+
public const string PACKAGE_STRING;
6+
public const string PACKAGE_VERSION;
7+
public const string GETTEXT_PACKAGE;
8+
9+
public const string LOCALEDIR;
10+
public const string PKGDATADIR;
11+
public const string PKGLIBDIR;
12+
}

‎wixl/msi.vala

+642
Large diffs are not rendered by default.

‎wixl/preprocessor.vala

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
namespace Wixl {
2+
3+
class Preprocessor: Object {
4+
5+
HashTable<string, string> globals;
6+
HashTable<string, string> variables;
7+
construct {
8+
variables = new HashTable<string, string> (str_hash, str_equal);
9+
}
10+
11+
public Preprocessor (HashTable<string, string> globals) {
12+
this.globals = globals;
13+
}
14+
15+
public void define_variable (string name, string value) {
16+
variables.insert (name, value);
17+
}
18+
19+
public string? lookup_variable (string name) {
20+
return variables.lookup (name) ?? globals.lookup (name);
21+
}
22+
23+
public string eval_variable (string str, File? file) throws GLib.Error {
24+
var var = str.split (".", 2);
25+
if (var.length != 2)
26+
throw new Wixl.Error.FAILED ("invalid variable %s", str);
27+
28+
switch (var[0]) {
29+
case "var":
30+
var val = lookup_variable (var[1]);
31+
if (val == null)
32+
throw new Wixl.Error.FAILED ("Undefined variable %s", var[1]);
33+
return val;
34+
case "env":
35+
return Environment.get_variable (var[1]);
36+
case "sys":
37+
switch (var[1]) {
38+
case "CURRENTDIR":
39+
return Environment.get_current_dir ();
40+
case "SOURCEFILEDIR":
41+
return file.get_basename ();
42+
case "SOURCEFILEPATH":
43+
return file.get_path ();
44+
}
45+
break;
46+
}
47+
48+
throw new Wixl.Error.FIXME ("unhandled variable type %s", str);
49+
}
50+
51+
public string eval (string str, File? file) throws GLib.Error {
52+
var result = "";
53+
int end = 0;
54+
int pos = 0;
55+
56+
while ((pos = str.index_of ("$", end)) != -1) {
57+
if (end < pos)
58+
result += str[end:pos];
59+
end = pos + 1;
60+
var remainder = str[end:str.length];
61+
if (remainder.has_prefix ("$"))
62+
result += "$";
63+
else if (remainder.has_prefix ("(")) {
64+
var closing = find_closing_paren (remainder);
65+
if (closing == -1)
66+
throw new Wixl.Error.FAILED ("no matching closing parenthesis");
67+
var substring = remainder[1:closing];
68+
if (substring.index_of ("(") != -1)
69+
throw new Wixl.Error.FIXME ("unsupported function");
70+
result += eval_variable (substring, file);
71+
end += closing + 1;
72+
}
73+
}
74+
75+
return result + str[end:str.length];
76+
}
77+
78+
public Xml.Doc preprocess (string data, File? file) throws GLib.Error {
79+
Xml.Doc doc;
80+
var writer = new Xml.TextWriter.doc (out doc);
81+
var reader = new Xml.TextReader.for_doc (data, "");
82+
83+
writer.start_document ();
84+
while (reader.read () > 0) {
85+
switch (reader.node_type ()) {
86+
case Xml.ReaderType.PROCESSING_INSTRUCTION:
87+
switch (reader.const_local_name ()) {
88+
case "define":
89+
MatchInfo info;
90+
var r = /^\s*(?P<name>.+?)\s*=\s*(?P<value>.+?)\s*$/;
91+
if (r.match (reader.const_value (), 0, out info)) {
92+
var name = remove_prefix ("var.", info.fetch_named ("name"));
93+
var value = unquote (info.fetch_named ("value"));
94+
define_variable (name, value);
95+
} else
96+
throw new Wixl.Error.FAILED ("invalid define");
97+
break;
98+
default:
99+
warning ("unhandled preprocessor instruction %s", reader.const_local_name ());
100+
break;
101+
}
102+
break;
103+
case Xml.ReaderType.ELEMENT:
104+
var empty = reader.is_empty_element () > 0;
105+
106+
writer.start_element (reader.const_name ());
107+
while (reader.move_to_next_attribute () > 0) {
108+
var value = eval (reader.const_value (), file);
109+
writer.write_attribute (reader.const_name (), value);
110+
}
111+
112+
if (empty)
113+
writer.end_element ();
114+
break;
115+
case Xml.ReaderType.END_ELEMENT:
116+
writer.end_element ();
117+
break;
118+
case Xml.ReaderType.TEXT:
119+
writer.write_string (eval (reader.const_value(), file));
120+
break;
121+
case Xml.ReaderType.CDATA:
122+
writer.write_cdata (eval (reader.const_value(), file));
123+
break;
124+
}
125+
}
126+
writer.end_document ();
127+
128+
return doc;
129+
}
130+
}
131+
}

‎wixl/util.vala

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
namespace Wixl {
2+
3+
public errordomain Error {
4+
FAILED,
5+
FIXME,
6+
}
7+
8+
namespace UUID {
9+
[CCode (cname = "uuid_generate", cheader_filename = "uuid/uuid.h")]
10+
internal extern static void generate ([CCode (array_length = false)] uchar[] uuid);
11+
[CCode (cname = "uuid_unparse", cheader_filename = "uuid/uuid.h")]
12+
internal extern static void unparse ([CCode (array_length = false)] uchar[] uuid,
13+
[CCode (array_length = false)] uchar[] output);
14+
}
15+
16+
string uuid_generate () {
17+
var udn = new uchar[50];
18+
var id = new uchar[16];
19+
20+
UUID.generate (id);
21+
UUID.unparse (id, udn);
22+
23+
return (string) udn;
24+
}
25+
26+
int enum_from_string (Type t, string str) throws GLib.Error {
27+
var k = (EnumClass)t.class_ref ();
28+
var v = k.get_value_by_nick (str);
29+
30+
if (v == null)
31+
throw new Wixl.Error.FAILED ("Can't convert string to enum");
32+
return v.value;
33+
}
34+
35+
string add_braces (string str) {
36+
if (str[0] == '{')
37+
return str;
38+
39+
return "{" + str + "}";
40+
}
41+
42+
string get_uuid (owned string uuid) throws GLib.Error {
43+
if (uuid == "*")
44+
uuid = uuid_generate ();
45+
uuid = add_braces (uuid);
46+
uuid = uuid.up ();
47+
// FIXME: validate
48+
return uuid;
49+
}
50+
51+
long now () {
52+
var tv = TimeVal ();
53+
tv.get_current_time ();
54+
return tv.tv_sec;
55+
}
56+
57+
uint64 time_to_filetime (long t) {
58+
return (t + 134774ULL * 86400ULL) * 10000000ULL;
59+
}
60+
61+
string get_attribute_content (Xml.Attr *attr) {
62+
if (attr->children == null)
63+
return "";
64+
65+
return attr->children->content;
66+
}
67+
68+
public string indent (string space, string text) {
69+
var indented = "";
70+
71+
foreach (var l in text.split ("\n")) {
72+
if (indented.length != 0)
73+
indented += "\n";
74+
75+
if (l.length != 0)
76+
indented += space + l;
77+
}
78+
79+
return indented;
80+
}
81+
82+
public string generate_id (string prefix, uint n, ...) {
83+
var l = va_list ();
84+
var args = new string[n];
85+
86+
for (var i = 0; n > 0; n--) {
87+
string? val = l.arg ();
88+
if (val == null)
89+
continue;
90+
args[i] = val; // FIXME: misc vala bug when +=
91+
i += 1;
92+
}
93+
var data = string.joinv ("|", args);
94+
var hash = Checksum.compute_for_string (ChecksumType.MD5, data);
95+
var str = prefix + hash[0:32].up ();
96+
97+
return str;
98+
}
99+
100+
bool parse_yesno (string? str, bool default = false) {
101+
if (str == null)
102+
return default;
103+
104+
return (str[0] == 'Y' || str[0] == 'y');
105+
}
106+
107+
string unquote (string str) {
108+
if ((str[0] == '\'' && str[str.length-1] == '\'') ||
109+
(str[0] == '"' && str[str.length-1] == '"'))
110+
return str[1:-1];
111+
112+
return str;
113+
}
114+
115+
string remove_prefix (string prefix, string str) {
116+
if (str.has_prefix (prefix))
117+
return str[prefix.length:str.length];
118+
119+
return str;
120+
}
121+
122+
int find_closing_paren (string str) {
123+
return_val_if_fail (str[0] == '(', -1);
124+
125+
var open_count = 1;
126+
var close_count = 0;
127+
for (var pos = 1; pos < str.length; pos++) {
128+
if (str[pos] == '(')
129+
open_count++;
130+
else if (str[pos] == ')') {
131+
close_count++;
132+
if (open_count == close_count)
133+
return pos;
134+
}
135+
}
136+
137+
return -1;
138+
}
139+
} // Wixl

‎wixl/wix.vala

+598
Large diffs are not rendered by default.

‎wixl/wixl.vala

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using Posix;
2+
3+
namespace Wixl {
4+
5+
static bool version;
6+
static bool verbose;
7+
static bool preproc;
8+
static string output;
9+
[CCode (array_length = false, array_null_terminated = true)]
10+
static string[] files;
11+
[CCode (array_length = false, array_null_terminated = true)]
12+
static string[] defines;
13+
14+
private const OptionEntry[] options = {
15+
{ "version", 0, 0, OptionArg.NONE, ref version, N_("Display version number"), null },
16+
{ "verbose", 'v', 0, OptionArg.NONE, ref verbose, N_("Verbose output"), null },
17+
{ "output", 'o', 0, OptionArg.FILENAME, ref output, N_("Output file"), null },
18+
{ "define", 'D', 0, OptionArg.STRING_ARRAY, ref defines, N_("Define variable"), null },
19+
{ "only-preproc", 'E', 0, OptionArg.NONE, ref preproc, N_("Stop after the preprocessing stage"), null },
20+
{ "", 0, 0, OptionArg.FILENAME_ARRAY, ref files, null, N_("INPUT_FILE...") },
21+
{ null }
22+
};
23+
24+
int main (string[] args) {
25+
Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
26+
Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
27+
Intl.textdomain (Config.GETTEXT_PACKAGE);
28+
GLib.Environment.set_application_name (Config.PACKAGE_NAME);
29+
30+
var parameter_string = _("- a msi building tool");
31+
var opt_context = new OptionContext (parameter_string);
32+
opt_context.set_help_enabled (true);
33+
opt_context.add_main_entries (options, null);
34+
35+
try {
36+
opt_context.parse (ref args);
37+
} catch (OptionError.BAD_VALUE err) {
38+
GLib.stdout.printf (opt_context.get_help (true, null));
39+
exit (1);
40+
} catch (OptionError error) {
41+
warning (error.message);
42+
}
43+
44+
if (version) {
45+
GLib.stdout.printf ("%s\n", Config.PACKAGE_VERSION);
46+
exit (0);
47+
}
48+
49+
if (files.length < 1) {
50+
GLib.stderr.printf (_("Please specify input files.\n"));
51+
exit (1);
52+
}
53+
54+
if (output == null && !preproc) {
55+
GLib.stderr.printf (_("Please specify the output file.\n"));
56+
exit (1);
57+
}
58+
59+
try {
60+
var builder = new WixBuilder ();
61+
62+
foreach (var d in defines) {
63+
var def = d.split ("=", 2);
64+
var name = def[0];
65+
var value = def.length == 2 ? def[1] : "1";
66+
builder.define_variable (name, value);
67+
}
68+
69+
foreach (var arg in files) {
70+
if (verbose)
71+
print ("Loading %s...\n", arg);
72+
var file = File.new_for_commandline_arg (arg);
73+
builder.load_file (file, preproc);
74+
builder.add_path (file.get_parent ().get_path ());
75+
}
76+
77+
if (preproc)
78+
return 0;
79+
80+
if (verbose)
81+
print ("Building %s...\n", output);
82+
var msi = builder.build ();
83+
msi.build (output);
84+
} catch (GLib.Error error) {
85+
printerr (error.message + "\n");
86+
return 1;
87+
}
88+
89+
return 0;
90+
}
91+
92+
} // Wixl

0 commit comments

Comments
 (0)
Please sign in to comment.