MSIBuild/MSI.wsf
Simon Rozman a53c4e2b87 Report .idt filename that threw exception
Signed-off-by: Simon Rozman <simon@rozman.si>
2021-11-19 13:45:23 +01:00

641 lines
22 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright © 1991-2021 Amebis
Copyright © 2016 GÉANT
This file is part of MSIBuild.
MSIBuild is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MSIBuild is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MSIBuild. If not, see <http://www.gnu.org/licenses/>.
-->
<package>
<job id="MakeDDF">
<runtime>
<description>Compile DDF file for shared CAB archive from input MSI files.</description>
<unnamed name="&lt;output.ddf&gt;" helpstring="Output DDF file" required="true"/>
<unnamed name="&lt;input.msi ...&gt;" helpstring="Input MSI files" required="true"/>
<named name="I" helpstring="Input files folder" type="string" required="false"/>
<named name="O" helpstring="CAB archive base name (default &quot;Dat&quot;)" type="string" required="false"/>
<named name="C" helpstring="Compression (default &quot;MSZIP&quot;)" type="string" required="false"/>
</runtime>
<script language="JScript" src="MSI.js"/>
<script language="JScript" src="String.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 2) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
fso = WScript.CreateObject("Scripting.FileSystemObject"),
ddf_path = WScript.Arguments.Unnamed(0);
try {
var
installer = WScript.CreateObject("WindowsInstaller.Installer"),
input_path = WScript.Arguments.Named.Exists("I") ? WScript.Arguments.Named("I") : null,
output_base = WScript.Arguments.Named.Exists("O") ? WScript.Arguments.Named("O") : "Dat",
compression = WScript.Arguments.Named.Exists("C") ? WScript.Arguments.Named("C") : "MSZIP",
files = new Array(),
i, n = WScript.Arguments.Unnamed.length;
// No user interaction is desired.
installer.UILevel = msiUILevelNone;
for (i = 1; i < n; i++) {
var
msi_path = WScript.Arguments.Unnamed(i),
d;
try {
d = MSIGetFiles(installer, msi_path, input_path);
MSIMergeFiles(files, d);
} catch (err) {
throw new Error(err.number, "" + msi_path + ": " + err.description);
}
// Force JScript engine to free database and session objects, otherwise opening of another session will fail.
CollectGarbage();
}
// Create DDF file and write header properties.
var
dat = fso.CreateTextFile(ddf_path, true, false);
try {
dat.WriteLine(".Set CabinetNameTemplate=" + output_base + "*.cab");
dat.WriteLine(".Set CabinetName1=" + output_base + ".cab");
dat.WriteLine(".Set InfFileName=" + output_base + ".inf");
dat.WriteLine(".Set RptFileName=" + output_base + ".rpt");
dat.WriteLine(".Set ReservePerCabinetSize=8");
dat.WriteLine(".Set MaxDiskSize=0");
dat.WriteLine(".Set CompressionType=" + compression);
dat.WriteLine(".Set InfFileLineFormat=(*disk#*) *file#*: *file* = *Size*");
dat.WriteLine(".Set InfHeader=");
dat.WriteLine(".Set InfFooter=");
dat.WriteLine(".Set InfDateFormat=yyyy-mm-dd");
dat.WriteLine(".Set DiskDirectoryTemplate=");
dat.WriteLine(".Set Compress=ON");
dat.WriteLine(".Set Cabinet=ON");
//if (input_path)
// dat.WriteLine(".Set SourceDir=" + input_path);
for (fileKey in files) {
var
f = fso.GetFile(files[fileKey]),
d = new Date(f.DateLastModified);
// Seconds should be even. This is MAKECAB limitation.
d.setSeconds(Math.round(d.getSeconds() / 2) * 2);
dat.WriteLine("\"" + files[fileKey] + "\" /Date=" + d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d.getDate() + " /Time=" + Time2Str(d) + " " + fileKey);
}
} finally {
dat.Close();
}
} catch (err) {
// In case of error, delete output file.
if (fso.FileExists(ddf_path))
fso.DeleteFile(ddf_path);
throw err;
}
WScript.Quit(0);
]]></script>
</job>
<job id="MakeDEP">
<runtime>
<description>Compile DEP dependency file from input MSI files.</description>
<unnamed name="&lt;output.dep&gt;" helpstring="Output DEP file" required="true"/>
<unnamed name="&lt;target file&gt;" helpstring="Target file dependant of input" required="true"/>
<unnamed name="&lt;input.msi ...&gt;" helpstring="Input MSI files" required="true"/>
<named name="I" helpstring="Input files folder" type="string" required="false"/>
</runtime>
<script language="JScript" src="MSI.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 3) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
fso = WScript.CreateObject("Scripting.FileSystemObject"),
dep_path = WScript.Arguments.Unnamed(0);
try {
var
installer = WScript.CreateObject("WindowsInstaller.Installer"),
input_path = WScript.Arguments.Named.Exists("I") ? WScript.Arguments.Named("I") : null,
target_path = WScript.Arguments.Unnamed(1),
files = new Array(),
i, n = WScript.Arguments.Unnamed.length;
// No user interaction is desired.
installer.UILevel = msiUILevelNone;
for (i = 2; i < n; i++) {
var
msi_path = WScript.Arguments.Unnamed(i),
d;
d = MSIGetFiles(installer, msi_path, input_path);
MSIMergeFiles(files, d);
// Force JScript engine to free database and session objects, otherwise opening of another session will fail.
CollectGarbage();
}
var
dat = fso.CreateTextFile(dep_path, true, false);
try {
dat.Write("\"" + target_path + "\" :");
for (fileKey in files)
dat.Write(" \\\r\n\t\"" + files[fileKey] + "\"");
dat.WriteLine();
dat.WriteLine();
} finally {
dat.Close();
}
} catch (err) {
// In case of error, delete output file.
if (fso.FileExists(dep_path))
fso.DeleteFile(dep_path);
throw err;
}
WScript.Quit(0);
]]></script>
</job>
<job id="SetCAB">
<runtime>
<description>Configures MSI package to use external CAB file.</description>
<unnamed name="&lt;database.msi&gt;" helpstring="Output MSI file" required="true"/>
<unnamed name="&lt;input.inf&gt;" helpstring="Input INF file" required="true"/>
<named name="I" helpstring="Input files folder" type="string" required="false"/>
<named name="E" helpstring="Embed CAB file" type="simple" required="false"/>
</runtime>
<script language="JScript" src="MSI.js"/>
<script language="JScript" src="String.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 2) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
installer = WScript.CreateObject("WindowsInstaller.Installer"),
fso = WScript.CreateObject("Scripting.FileSystemObject"),
input_path = WScript.Arguments.Named.Exists("I") ? WScript.Arguments.Named("I") : "",
embed = WScript.Arguments.Named.Exists("E") ? true : false,
msi_path = WScript.Arguments.Unnamed(0),
inf_path = WScript.Arguments.Unnamed(1),
cab_path = null,
cab_name;
// No user interaction is desired.
installer.UILevel = msiUILevelNone;
var
database = installer.OpenDatabase(msi_path, msiOpenDatabaseModeTransact),
session = installer.OpenPackage(database, 1),
shortNames = session.Mode(msiRunModeSourceShortNames),
lastSequence = 0,
stat,
view,
record,
updateMode,
sumInfo,
today = new Date();
if (input_path)
session.Property("OriginalDatabase") = input_path;
stat = session.DoAction("CostInitialize");
if (stat != 1)
throw new Error("Error calling CostInitialize() (code " + stat + ").");
var
dat = fso.OpenTextFile(inf_path, 1);
try {
var
section = null,
re_section = new RegExp("^\\s*\\[([^\\]]*)\\]\\s*$", "i"),
re_file_list = new RegExp("^\\s*\\((\\d+)\\)\\s*(\\d+)\\s*[:]([^=]+)[=]\\s*(\\d+)\\s*$", "i"),
re_cabinet_list = new RegExp("^\\s*(\\d+)\\s*[,]\\s*(\\d+)\\s*[,](.*)$", "i");
while (!dat.AtEndOfStream) {
var
s = new String(dat.ReadLine()),
m;
if (m = s.match(re_section)) {
// We found a section.
section = new String(m[1]);
} else if (section.toLowerCase() == "file list") {
if (m = s.match(re_file_list)) {
var
fileNum = parseInt(m[2], 10),
fileId = Trim(m[3]);
// Set file sequence in database.
view = database.OpenView("SELECT Sequence,Attributes FROM File WHERE File='" + _S(fileId) + "'");
view.Execute();
record = view.Fetch();
if (record) {
record.IntegerData(1) = fileNum;
// record.IntegerData(2) = (record.IntegerData(2) & ~msidbFileAttributesNoncompressed) | msidbFileAttributesCompressed;
view.Modify(msiViewModifyUpdate, record);
}
view.Close();
if (fileNum > lastSequence)
lastSequence = fileNum;
}
} else if (section.toLowerCase() == "cabinet list") {
if (m = s.match(re_cabinet_list)) {
if (cab_path)
throw new Error("INF file can contain one CAB archive only.");
// Read archive file name.
cab_path = Trim(m[3]);
cab_name = cab_path.split("\\");
cab_name = cab_name[cab_name.length - 1];
}
}
}
} finally {
dat.Close();
}
var cab_id = embed ? _MSI(cab_name) : cab_name;
view = database.OpenView("SELECT DiskId,LastSequence,Cabinet FROM Media ORDER BY DiskId");
view.Execute();
record = view.Fetch();
updateMode = msiViewModifyUpdate;
if (!record) {
// Media table is empty.
record = installer.CreateRecord(3);
record.IntegerData(1) = 1;
updateMode = msiViewModifyInsert;
}
record.IntegerData(2) = lastSequence;
record.StringData(3) = (embed ? "#" : "") + cab_id;
view.Modify(updateMode, record);
sumInfo = database.SummaryInformation(3);
sumInfo.Property(PID_LASTPRINTED) = today.getVarDate();
sumInfo.Property(PID_LASTSAVE_DTM) = today.getVarDate();
sumInfo.Property(PID_WORDCOUNT) = (shortNames ? 1 : 0) | 2;
sumInfo.Persist();
if (embed) {
view = database.OpenView("SELECT Name,Data FROM _Streams");
view.Execute();
record = installer.CreateRecord(2);
record.StringData(1) = cab_id;
record.SetStream(2, cab_path);
view.Modify(msiViewModifyAssign, record);
}
// Commit database
database.Commit();
WScript.Quit(0);
]]></script>
</job>
<job id="MakeMST">
<runtime>
<description>Build MSI language from differences between two MSI files.</description>
<unnamed name="&lt;source.msi&gt;" helpstring="Original MSI file" required="true"/>
<unnamed name="&lt;destination.msi&gt;" helpstring="Updated MSI file" required="true"/>
<unnamed name="&lt;diff.mst&gt;" helpstring="Output MST file" required="true"/>
</runtime>
<script language="JScript" src="MSI.js"/>
<script language="JScript" src="String.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 3) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
fso = WScript.CreateObject("Scripting.FileSystemObject"),
mst_path = WScript.Arguments.Unnamed(2);
try {
var
installer = WScript.CreateObject("WindowsInstaller.Installer"),
db_src = installer.OpenDatabase(WScript.Arguments.Unnamed(0), msiOpenDatabaseModeReadOnly),
db_dst = installer.OpenDatabase(WScript.Arguments.Unnamed(1), msiOpenDatabaseModeReadOnly);
db_dst.GenerateTransform(db_src, mst_path);
db_dst.CreateTransformSummaryInfo(db_src, mst_path, msiTransformErrorNone, msiTransformValidationNone);
} catch (err) {
// In case of error, delete output file.
if (fso.FileExists(mst_path))
fso.DeleteFile(mst_path);
throw err;
}
WScript.Quit(0);
]]></script>
</job>
<job id="AddStorage">
<runtime>
<description>Adds sub-storage to MSI file.</description>
<unnamed name="&lt;database.msi&gt;" helpstring="MSI database file" required="true"/>
<unnamed name="&lt;transform.mst&gt;" helpstring="MST file to add as a sub-storage" required="true"/>
<unnamed name="&lt;name&gt;" helpstring="Sub-storage name" required="true"/>
<named name="L" helpstring="Add transform to the list of language transforms" type="boolean" required="false"/>
</runtime>
<script language="JScript" src="MSI.js"/>
<script language="JScript" src="String.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 3) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
fso = WScript.CreateObject("Scripting.FileSystemObject"),
msi_path = WScript.Arguments.Unnamed(0);
try {
var
installer = WScript.CreateObject("WindowsInstaller.Installer"),
database = installer.OpenDatabase(msi_path, msiOpenDatabaseModeTransact),
view = database.OpenView("SELECT Name,Data FROM _Storages"),
record = installer.CreateRecord(2),
stream_path = WScript.Arguments.Unnamed(1),
stream_name = WScript.Arguments.Unnamed(2);
try {
// Inserts or updates sub-storage.
record.StringData(1) = stream_name;
view.Execute(record);
record.SetStream(2, stream_path);
view.Modify(msiViewModifyAssign, record);
} catch (err) {
throw new Error(err.number, "" + stream_path + ": " + err.description);
}
if (WScript.Arguments.Named.Exists("L") && WScript.Arguments.Named("L") != false) {
// Get platform.
var
sumInfo = database.SummaryInformation(1),
template = sumInfo.Property(PID_TEMPLATE),
idx_delim = template.indexOf(";"),
platform = idx_delim >= 0 ? template.substring(0, idx_delim + 1) : ";",
languages = idx_delim >= 0 ? template.substring(idx_delim + 1).split(new RegExp("\\s*,\\s*")) : new Array();
languages.push(stream_name);
// Set template property.
sumInfo.Property(PID_TEMPLATE) = platform + languages.join(",");
sumInfo.Persist();
}
// Commit database
database.Commit();
} catch (err) {
// In case of error, delete output file.
if (fso.FileExists(msi_path))
fso.DeleteFile(msi_path);
throw err;
}
WScript.Quit(0);
]]></script>
</job>
<job id="IDTDiff">
<runtime>
<description>Extract strings to translate by comparing two IDT files.</description>
<unnamed name="&lt;output.po&gt;" helpstring="Output PO file" required="true"/>
<unnamed name="&lt;source.idt&gt;" helpstring="Original IDT file" required="true"/>
<unnamed name="&lt;destination.idt&gt;" helpstring="Translated IDT file" required="true"/>
<named name="L" helpstring="Language identifier" type="string" required="false"/>
</runtime>
<reference object="ADODB.Stream"/>
<reference object="Scripting.FileSystemObject"/>
<script language="JScript" src="IDT.js"/>
<script language="JScript" src="PO.js"/>
<script language="JScript" src="String.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 3) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
fso = WScript.CreateObject("Scripting.FileSystemObject"),
po_path = WScript.Arguments.Unnamed(0);
try {
// Open and parse IDT file(s).
var
idt_src_path = WScript.Arguments.Unnamed(1),
idt_src = new IDT(idt_src_path),
idt_dst = new IDT(WScript.Arguments.Unnamed(2));
// IDT files must represent identical table.
if (idt_src.columns.toString() != idt_dst.columns.toString() ||
idt_src.types .toString() != idt_dst.types .toString() ||
idt_src.table != idt_dst.table ||
idt_src.key .toString() != idt_dst.key .toString())
throw new Error("IDT files are not compatible.");
var po = new POCatalog();
// Add header.
po.push("",
"MIME-Version: 1.0\n" +
"Content-Type: text/plain; charset=" + po.charset + "\n" +
"Content-Transfer-Encoding: 8bit\n" +
(WScript.Arguments.Named.Exists("L") ? "Language: "+ WScript.Arguments.Named("L") +"\n" : ""), false);
// Build translations.
for (var key in idt_src.data) {
if (key in idt_dst.data) {
// We found a record in both IDT files.
for (var col in idt_src.types)
if (idt_src.isLocalizable(col) && idt_src.data[key][col] != "")
po.push(idt_src.data[key][col], idt_dst.data[key][col], false, "#: " + idt_src_path + ":" + idt_src.linenum[key]);
} else {
// The record is available in the source IDT file only.
for (var col in idt_src.types)
if (idt_src.isLocalizable(col) && idt_src.data[key][col] != "")
po.push(idt_src.data[key][col], "", false, "#: " + idt_src_path + ":" + idt_src.linenum[key]);
}
}
// Save to file.
po.save(po_path);
} catch (err) {
// In case of error, delete output file.
if (fso.FileExists(po_path))
fso.DeleteFile(po_path);
throw err;
}
WScript.Quit(0);
]]></script>
</job>
<job id="IDTExtract">
<runtime>
<description>Extract strings to translate.</description>
<unnamed name="&lt;output.po&gt;" helpstring="Output PO file" required="true"/>
<unnamed name="&lt;source.idt&gt;..." helpstring="Source IDT files" required="true"/>
</runtime>
<reference object="ADODB.Stream"/>
<reference object="Scripting.FileSystemObject"/>
<script language="JScript" src="IDT.js"/>
<script language="JScript" src="PO.js"/>
<script language="JScript" src="String.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 2) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
fso = WScript.CreateObject("Scripting.FileSystemObject"),
po_path = WScript.Arguments.Unnamed(0);
try {
var po = new POCatalog();
// Add header.
po.push("",
"MIME-Version: 1.0\n" +
"Content-Type: text/plain; charset=" + po.charset + "\n" +
"Content-Transfer-Encoding: 8bit\n", false);
// Build translations.
for (var i = 1, argc = WScript.Arguments.Unnamed.Length; i < argc; i++) {
try {
// Open and parse IDT file(s).
var
idt_src_path = WScript.Arguments.Unnamed(i),
idt_src = new IDT(idt_src_path);
for (var key in idt_src.data) {
for (var col in idt_src.types)
if (idt_src.isLocalizable(col) && idt_src.data[key][col] != "") {
var src = idt_src.data[key][col];
var t = po.search(src);
if (t) {
// Text-to-translate already present. Add source to the list.
t["#"] += " " + idt_src_path + ":" + idt_src.linenum[key];
} else {
// Add text-to-translate.
po.push(src, "", false, "#: " + idt_src_path + ":" + idt_src.linenum[key]);
}
}
}
} catch (err) {
throw new Error(err.number, "" + WScript.Arguments.Unnamed(i) + ": " + err.description);
}
}
// Save to file.
po.save(po_path);
} catch (err) {
// In case of error, delete output file.
if (fso.FileExists(po_path))
fso.DeleteFile(po_path);
throw err;
}
WScript.Quit(0);
]]></script>
</job>
<job id="IDTTranslate">
<runtime>
<description>Translate IDT file.</description>
<unnamed name="&lt;destination.idt&gt;" helpstring="Translated IDT file" required="true"/>
<unnamed name="&lt;source.idt&gt;" helpstring="Original IDT file" required="true"/>
<unnamed name="&lt;catalogue.po&gt;" helpstring="PO catalog file with translations" required="false"/>
<named name="CP" helpstring="Output code page" type="string" required="false"/>
</runtime>
<reference object="ADODB.Stream"/>
<reference object="Scripting.FileSystemObject"/>
<script language="JScript" src="IDT.js"/>
<script language="JScript" src="PO.js"/>
<script language="JScript" src="String.js"/>
<script language="JScript"><![CDATA[
if (WScript.Arguments.Unnamed.Length < 2) {
WScript.Arguments.ShowUsage();
WScript.Quit(1);
}
var
fso = WScript.CreateObject("Scripting.FileSystemObject"),
idt_dst_path = WScript.Arguments.Unnamed(0);
try {
// Open and parse source IDT file.
var idt_src = new IDT(WScript.Arguments.Unnamed(1));
if (WScript.Arguments.Unnamed.Length >= 3) {
// Translate records.
var po = new POCatalog(WScript.Arguments.Unnamed(2));
for (var key in idt_src.data) {
for (var col in idt_src.types) {
if (idt_src.isLocalizable(col) && idt_src.data[key][col] != "")
idt_src.data[key][col] = po.translate(idt_src.data[key][col]);
}
}
}
if (WScript.Arguments.Named.Exists("CP"))
idt_src.codepage = parseInt(WScript.Arguments.Named("CP"), 10);
idt_src.save(idt_dst_path);
} catch (err) {
// In case of error, delete output file.
if (fso.FileExists(idt_dst_path))
fso.DeleteFile(idt_dst_path);
throw err;
}
WScript.Quit(0);
]]></script>
</job>
<job id="MakeGUID">
<runtime>
<description>Creates a new GUID and displays it on output.</description>
<named name="M" helpstring="Makefile macro name" type="string" required="false"/>
</runtime>
<reference object="Scriptlet.TypeLib"/>
<script language="JScript"><![CDATA[
var
tlib = WScript.CreateObject("Scriptlet.TypeLib"),
guid = tlib.Guid;
WScript.Echo(
WScript.Arguments.Named.Exists("M") ? WScript.Arguments.Named("M") + "=" + guid.toUpperCase() :
guid.toUpperCase());
WScript.Quit(0);
]]></script>
</job>
</package>