Files
raven/scripts/provision-installer.py
T
2026-10-31 14:09:33 +00:00

588 lines
49 KiB
Python

#!/usr/bin/env python3
import argparse
from concurrent.futures import ThreadPoolExecutor
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
import tarfile
import urllib.request
from component_licenses import mark_patched_sources, preserve_notices
# The ReactOS revision the common controls component is built from: one archive serves both.
REVISION = "dacf2a6c47fa562799f9c8a41ab6d5c558556558"
ARCHIVE_SHA256 = "5373bbbb60541cb2a49e0acb53360e1218d4f5d56c31f9187b341b9fc16daa69"
PREFIXES = ("sdk/include/", "sdk/lib/pseh/", "sdk/tools/hpp/", "sdk/tools/spec2def/", "sdk/lib/3rdparty/zlib/", "sdk/lib/3rdparty/libwine/",
"dll/win32/msi/",
"base/system/msiexec/", "sdk/lib/3rdparty/libxml2/", "sdk/lib/3rdparty/libwin-iconv/", "dll/3rdparty/libxslt/",
"dll/win32/stdole2.tlb/",
"dll/3rdparty/libpng/", "dll/3rdparty/libjpeg/", "dll/3rdparty/libtiff/",
"sdk/lib/getopt/", "dll/win32/setupapi/", "dll/win32/kernel32/wine/lzexpand.c")
# Interface headers the installer sources include, generated from the public IDL files with what they import.
IDLS = ("objidlbase", "commoncontrols", "fusion", "msxml2", "oaidl", "ocidl", "objidl", "unknwn", "wtypes", "propidl", "urlmon", "shobjidl", "servprov", "docobj", "oleidl",
"exdisp", "activscp", "shldisp", "mshtml", "xmldom", "imnact", "msctf", "shtypes", "oleacc", "comcat", "mscoree", "wincodec", "dispex",
"atliface", "winsxs", "msxml6", "msxml", "xmldso", "objsafe", "mshtmhst", "mshtmdid", "httprequest", "xmlparser", "asptlb", "perhist", "hlink", "mimeinfo", "mlang", "wincodec", "wincodecsdk", "propsys", "structuredquery", "dxgiformat", "dxgicommon", "dxgitype", "xmllite")
ZLIB = ("adler32.c", "crc32.c", "deflate.c", "inflate.c", "inffast.c", "inftrees.c", "trees.c", "zutil.c")
COMPONENTS = ("cabinet.dll", "sxs.dll", "rpcrt4.dll", "msi.dll", "raven-storage.dll", "msiexec.exe", "msxml3.dll", "atl100.dll", "oleaut32.dll", "libpng.dll", "libjpeg.dll",
"libtiff.dll", "propsys.dll", "mlang.dll", "windowscodecs.dll", "gdiplus.dll", "urlmon.dll", "riched20.dll", "msftedit.dll", "msvfw32.dll", "advpack.dll", "raven-setupapi.dll", "xmllite.dll")
def main():
parser = argparse.ArgumentParser(description="Build the pinned ReactOS Windows Installer components: msi, cabinet and sxs")
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--archive", type=Path)
parser.add_argument("--arch", choices=("x64", "x86"), default="x64", help="build the 64-bit components, or the 32-bit ones for PE32 programs")
args = parser.parse_args()
x86 = args.arch == "x86"
triple = "i686-w64-mingw32" if x86 else "x86_64-w64-mingw32"
spec_arch = "-a=i386" if x86 else "-a=x86_64"
entry_point = "_DllMain@12" if x86 else "DllMain"
link_extra = ["-Wl,--kill-at"] if x86 else []
bases = dict(cabinet=0x1e000000, sxs=0x1e200000, rpcrt4=0x1d800000, msi=0x1e400000, storage=0x1ec00000, xslt=0x1d400000, msxml=0x1d000000, atl=0x1cc00000, oleaut=0x1c800000) if x86 else dict(
cabinet=0x1e0000000, sxs=0x1e2000000, rpcrt4=0x1d8000000, msi=0x1e4000000, storage=0x1ec000000, xslt=0x1d4000000, msxml=0x1d0000000, atl=0x1cc000000, oleaut=0x1c8000000)
output = args.output.resolve()
output.mkdir(parents=True, exist_ok=True)
env = dict(os.environ, TMPDIR=str(output), TMP=str(output), TEMP=str(output))
repo = Path(__file__).resolve().parents[1]
archive = args.archive.resolve() if args.archive else output / "reactos-source.tar.gz"
if not archive.exists():
urllib.request.urlretrieve(f"https://codeload.github.com/reactos/reactos/tar.gz/{REVISION}", archive)
if hashlib.sha256(archive.read_bytes()).hexdigest() != ARCHIVE_SHA256:
raise RuntimeError("ReactOS source archive checksum differs")
source = output / "source"
shutil.rmtree(source, ignore_errors=True)
with tarfile.open(archive) as upstream:
for member in upstream:
parts = Path(member.name).parts[1:]
name = "/".join(parts)
if not member.isfile() or not (name.startswith(PREFIXES) or ("/" not in name and name.startswith("COPYING"))):
continue
if not parts or ".." in parts or Path(name).is_absolute():
raise RuntimeError("invalid upstream archive path")
target = source / name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(upstream.extractfile(member).read())
for tool in ("clang", triple + "-gcc", triple + "-dlltool", "widl", "bison"):
if not shutil.which(tool):
raise RuntimeError(f"required installer build tool is missing: {tool}")
records = []
def run(command, cwd=None):
result = subprocess.run(command, cwd=cwd, env=env, capture_output=True, text=True)
records.append(dict(command=[str(part) for part in command], returncode=result.returncode))
if result.returncode:
raise RuntimeError(result.stderr or "installer build command failed: " + " ".join(map(str, command)))
return result
def decorated(object_files):
"""Defined global functions of the objects by plain name, with their stdcall argument bytes on 32 bits (None for the others)."""
names = {}
for line in run([triple + "-nm", "-g", "--defined-only", *object_files]).stdout.splitlines():
parts = line.split()
if len(parts) != 3 or parts[1] not in "Tt":
continue
symbol = parts[2]
if x86:
match = re.fullmatch(r"_(\w+)@(\d+)", symbol)
if match:
names[match.group(1)] = int(match.group(2))
elif symbol.startswith("_"):
names.setdefault(symbol[1:], None)
else:
names[symbol] = None
return names
# Raven's corrections to upstream behavior are kept as reviewable patches and recorded in the provenance.
support = repo / "native/upstream/reactos-installer"
patches = sorted((support / "patches").glob("*.patch"))
for patch in patches:
run(["patch", "-p1", "--forward", "--silent", "-i", patch], source)
mark_patched_sources(source, patches)
generated = output / "generated"
shutil.rmtree(generated, ignore_errors=True)
(generated / "psdk").mkdir(parents=True)
(generated / "reactos").mkdir()
run(["clang", "-O2", "-w", source / "sdk/tools/hpp/hpp.c", "-o", output / "hpp"])
run(["clang", "-O1", "-w", source / "sdk/tools/spec2def/spec2def.c", "-o", output / "spec2def"])
for name in ("winnt", "ntdef", "devioctl"):
run([output / "hpp", name + ".template.h", generated / "psdk" / (name + ".h")], source / "sdk/include/xdk")
widl = ["widl", "-m32" if x86 else "-m64", "-D__REACTOS__", "-I", source / "sdk/include/psdk", "-I", source / "sdk/include", "-I", generated / "psdk"]
idls = set()
def idl(name):
path = source / "sdk/include/psdk" / (name + ".idl")
if name in idls or not path.exists():
return
idls.add(name)
for dependency in re.findall(r'import\s+"([^"]+)\.idl"', path.read_text(errors="replace")):
idl(dependency)
run([*widl, "-h", "-H", generated / "psdk" / (name + ".h"), path])
for name in IDLS:
idl(name)
# Every other public interface header too, as the components include many of them; one widl cannot read is left out.
for path in sorted((source / "sdk/include/psdk").glob("*.idl")):
try:
idl(path.stem)
except RuntimeError:
pass
# The version headers are configure-time templates upstream; the component pins their values.
version = (source / "sdk/include/reactos/version.h.cmake").read_text()
for key, value in (("KERNEL_VERSION_MAJOR", "0"), ("KERNEL_VERSION_MINOR", "4"), ("KERNEL_VERSION_PATCH_LEVEL", "17"), ("COPYRIGHT_YEAR", "2026")):
version = version.replace("@" + key + "@", value)
(generated / "version.h").write_text(version)
(generated / "reactos/version.h").write_text(version)
buildno = (source / "sdk/include/reactos/buildno.h.cmake").read_text()
for key, value in (("KERNEL_VERSION_BUILD", "custom"), ("REVISION", REVISION[:7]), ("KERNEL_VERSION", "0.4.17-dev"), ("COMMIT_HASH", REVISION),
("REACTOS_DLL_VERSION_MAJOR", "42"), ("DLL_VERSION_STR", "42.4.17-dev"), ("CMAKE_C_COMPILER_ID", "GNU"), ("CMAKE_C_COMPILER_VERSION", "13")):
buildno = buildno.replace("@" + key + "@", value)
(generated / "reactos/buildno.h").write_text(buildno)
# The Wine-compat header overlay sits one directory below its psdk siblings upstream; Wine modules see it first.
overlay = source / "sdk/include/reactos/wine"
shutil.copytree(source / "sdk/include/wine", overlay, dirs_exist_ok=True)
gcc_include = run([triple + "-gcc", "-print-file-name=include"]).stdout.strip()
# A toolchain whose default C runtime is the UCRT gives that library the name msvcrt and keeps the one of msvcrt.dll as msvcrt-os.
msvcrt = "-lmsvcrt-os" if Path(run([triple + "-gcc", "-print-file-name=libmsvcrt-os.a"]).stdout.strip()).is_absolute() else "-lmsvcrt"
includes = [generated, generated / "psdk", source / "sdk/lib/pseh/include", source / "sdk/include/vcruntime", overlay, source / "sdk/include/psdk",
source / "sdk/include/crt", source / "sdk/include/ndk", source / "sdk/include", source / "sdk/include/reactos", source / "sdk/include/ddk"]
# A function the headers do not declare would return a truncated int: none is allowed. The sources use the old CRT names, which the
# oldnames library supplies upstream.
# GCC 14 and later reject the pointer mismatches the upstream sources still carry, such as an extended version structure passed as its
# base; they stay warnings, as with the compilers the sources are built with upstream.
flags = ["-O2", "-nostdinc", "-isystem", gcc_include, "-fgnu89-inline", "-Werror=implicit-function-declaration", "-Werror=int-conversion",
"-Wno-error=incompatible-pointer-types",
"-Dwcsdup=_wcsdup", "-Dwcsicmp=_wcsicmp", "-Dwcsnicmp=_wcsnicmp", "-Dwcsupr=_wcsupr", "-Dwcslwr=_wcslwr", "-Dstrdup=_strdup", "-Dstricmp=_stricmp", "-Dstrnicmp=_strnicmp",
"-D_WIN32_WINNT=0x600", "-DWINVER=0x600", "-D_WIN32_IE=0x0A00", "-D__REACTOS__", "-DUNICODE", "-D_UNICODE",
"-D_M_IX86" if x86 else "-D_M_AMD64", "-D__WINESRC__", "-D_WINE", *([] if x86 else ["-D__ROS_LONG64__"]),
# 32-bit handlers read the exception through the frame based PSEH of ReactOS, as the guarded blocks themselves do.
*(["-D_exception_code()=_SEH2_GetExceptionCode()", "-D_exception_info()=_SEH2_GetExceptionInformation()"] if x86 else []),
f"-ffile-prefix-map={source}=reactos", f"-ffile-prefix-map={repo}=raven", f"-ffile-prefix-map={output}=component"]
objects = output / "objects"
shutil.rmtree(objects, ignore_errors=True)
objects.mkdir()
def compile(path, name, extra=(), wine_module=True):
target = objects / (name + ".o")
paths = includes if wine_module else [path for path in includes if path != overlay]
run([triple + "-gcc", "-c", *flags, *extra, *["-I" + str(path) for path in paths], path, "-o", target])
return target
def runtime(libraries):
return [msvcrt if library == "-lmsvcrt" else library for library in libraries]
def link(name, base, object_files, definition, libraries, implib=None):
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{entry_point},--image-base,{base:#x},--no-insert-timestamp", *link_extra,
*([f"-Wl,--out-implib,{implib}"] if implib and not x86 else []), "-o", output / name, *object_files, definition,
output / "libcrtimports.a", *runtime(libraries)])
# A 32-bit import library keeps the stdcall decorations its callers link against, for the plain names the library exports.
if implib and x86:
stdcall = output / (Path(name).stem + "-import.def")
stdcall.write_text(re.sub(r"^(\s*)(\w+)=\w+(@\d+)", r"\1\2\3", Path(definition).read_text(), flags=re.M))
run([triple + "-dlltool", "-d", stdcall, "-l", implib, "-k"])
# Import these CRT functions directly; newer MinGW libraries replace them with static wrappers.
imports = output / "crtimports.def"
imports.write_text("LIBRARY msvcrt.dll\nEXPORTS\n _assert\n iswctype\n isprint\n _gmtime64\n")
run([triple + "-dlltool", "-d", imports, "-l", output / "libcrtimports.a"])
# Newer 32-bit MinGW libraries import the LZ expansion functions from lz32, a forwarder; kernel32 exports them.
expansion = output / "lzimports.def"
expansion.write_text("LIBRARY kernel32.dll\nEXPORTS\n" + "".join(" " + name + ("@" + size if x86 else "") + "\n" for name, size in
(("LZOpenFileW", "12"), ("LZRead", "12"), ("LZSeek", "12"), ("LZClose", "4"))))
run([triple + "-dlltool", "-d", expansion, "-l", output / "liblzimports.a", *(["-k"] if x86 else [])])
debug = compile(repo / "native/upstream/reactos-installer/debug.c", "debug")
# 32-bit code registers its exception frames on the thread's chain: the frame based PSEH support of ReactOS is built into each component.
pseh = []
if x86:
pseh_root = source / "sdk/lib/pseh"
for name in ("framebased-gcchack.c", "framebased-gcchack-asm.S", "framebased.S", "../framebased.c"):
target = objects / ("pseh_" + Path(name).name.replace(".", "_") + ".o")
language = ["-x", "assembler-with-cpp"] if name.endswith(".S") else [*flags, *["-I" + str(path) for path in includes]]
run([triple + "-gcc", "-c", *language, "-I" + str(pseh_root / "include"), "-I" + str(pseh_root / "include/pseh"), pseh_root / "i386" / name,
"-o", target])
pseh.append(target)
debug = [debug, *pseh]
else:
debug = [debug]
# cabinet.dll: cabinet files, their compression and extraction, on zlib.
zlib_include = source / "sdk/include/reactos/libs/zlib"
zlib = [compile(source / "sdk/lib/3rdparty/zlib" / name, "zlib_" + Path(name).stem, ["-I" + str(zlib_include)]) for name in ZLIB]
cabinet_root = repo / "native/upstream/raven-cabinet"
with ThreadPoolExecutor(max_workers=2) as pool:
cabinet = list(pool.map(lambda name: compile(cabinet_root / name, "cabinet_" + Path(name).stem,
["-I" + str(cabinet_root), "-I" + str(zlib_include)], wine_module=False),
("compose.c", "extract.c", "lzx.c", "quantum.c", "session.c")))
run([output / "spec2def", spec_arch, "-n=cabinet.dll", "-d=" + str(output / "cabinet.def"), cabinet_root / "cabinet.spec"])
link("cabinet.dll", bases["cabinet"], [*zlib, *cabinet, *debug], output / "cabinet.def", ["-lmsvcrt", "-lkernel32", "-lntdll", "-lgcc"],
output / "libcabinet.a")
# sxs.dll: the side-by-side assembly cache.
own_sxs = repo / "native/upstream/raven-sxs"
(output / "sxs.def").write_text("LIBRARY sxs.dll\nEXPORTS\n" + (own_sxs / "exports.txt").read_text())
run([triple + "-gcc", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-DCONST_VTABLE", "-shared", "-nostdlib",
f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{bases['sxs']:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "sxs.dll", own_sxs / "sxs.c", repo / "native/upstream/raven-comctl32/crt.c", output / "sxs.def", "-luuid", "-lole32", "-loleaut32", "-luser32", "-lkernel32", "-lgcc"])
# The installer engine links against the callers' view of these: on x86 their names carry their argument sizes.
(output / "sxs_import.def").write_text("LIBRARY sxs.dll\nEXPORTS\n" + "".join(f" {name}{'@' + str(size) if x86 else ''}\n" for name, size in
(("CreateAssemblyCache", 8), ("CreateAssemblyNameObject", 16), ("SxsLookupClrGuid", 24), ("SxspGenerateManifestPathOnAssemblyIdentity", 16))))
run([triple + "-dlltool", *(["-k"] if x86 else []), "-d", output / "sxs_import.def", "-l", output / "libsxs.a"])
compile(support / "support.c", "installer_support")
# rpcrt4.dll: Raven's own remote procedure calls, which the installer's custom action server speaks over local pipes.
own_rpc = repo / "native/upstream/raven-rpcrt4"
rpc_objects = []
for path in [*sorted(own_rpc.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("rpcrt4_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-D_UNICODE", "-DCOBJMACROS", "-D_RPCRT4_", "-I" + str(own_rpc), "-o", target, path])
rpc_objects.append(target)
rpc_exports = (own_rpc / "exports.txt").read_text()
(output / "rpcrt4.def").write_text("LIBRARY rpcrt4.dll\nEXPORTS\n" + rpc_exports)
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{bases['rpcrt4']:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "rpcrt4.dll", *rpc_objects, output / "rpcrt4.def", "-lole32", "-loleaut32", "-luuid", "-luser32", "-ladvapi32", "-lkernel32", "-lntdll", "-lgcc"])
rpc_defined = decorated(rpc_objects)
(output / "rpcrt4_import.def").write_text("LIBRARY rpcrt4.dll\nEXPORTS\n" + "".join(
f" {name}{'' if rpc_defined.get(name) is None else '@' + str(rpc_defined[name])}\n" for name in (line.split()[0] for line in rpc_exports.splitlines() if line.strip()) if "PRIVATE" not in name))
run([triple + "-dlltool", *(["-k"] if x86 else []), "-d", output / "rpcrt4_import.def", "-l", output / "librpcrt4.a"])
# msi.dll: the Windows Installer engine, its SQL and condition grammars, and the RPC stubs of its custom action server.
msi_root = source / "dll/win32/msi"
run(["bison", "-o", generated / "cond.tab.c", "-d", msi_root / "cond.y"])
run(["bison", "-o", generated / "sql.tab.c", "-d", msi_root / "sql.y"])
msi_widl = [*widl, "-I", msi_root, "--prefix-server=s_"]
for name in ("msiserver", "winemsi"):
run([*msi_widl, "-h", "-H", generated / (name + ".h"), msi_root / (name + ".idl")])
run([*msi_widl, "-u", "-U", generated / (name + "_i.c"), msi_root / (name + ".idl")])
run([*msi_widl, "-c", "-C", generated / "winemsi_c.c", msi_root / "winemsi.idl"])
run([*msi_widl, "-s", "-S", generated / "winemsi_s.c", msi_root / "winemsi.idl"])
run([output / "spec2def", spec_arch, "-n=msi.dll", "-d=" + str(output / "msi.def"), "-s=" + str(generated / "msi_stubs.c"), msi_root / "msi.spec"])
msi_sources = sorted(msi_root.glob("*.c")) + [generated / name for name in ("cond.tab.c", "sql.tab.c", "msiserver_i.c", "winemsi_c.c",
"winemsi_s.c", "msi_stubs.c")]
msi_flags = ["-DMSIRUNMODE=MSIRUNMODE_T", "-I" + str(msi_root)]
with ThreadPoolExecutor(max_workers=2) as pool:
msi = list(pool.map(lambda path: compile(path, "msi_" + path.stem, msi_flags, wine_module=False), msi_sources))
msi.append(compile(source / "sdk/lib/3rdparty/libwine/register.c", "wine_register", ["-I" + str(msi_root)]))
# Its strings, bitmaps, registration script and version: installers check that version before they run.
wrapper = output / "resource-preprocessor.sh"
wrapper.write_text("#!/bin/sh\nexec " + triple + "-gcc -E -xc -DRC_INVOKED -nostdinc -isystem " + gcc_include + " " +
" ".join("-I" + str(path) for path in [msi_root, *includes, generated / "reactos"] if path != overlay) + ' -w "$@"\n')
wrapper.chmod(0o755)
run([triple + "-windres", "--preprocessor=" + str(wrapper), "-O", "coff", "-DLANGUAGE_EN_US", "-D__REACTOS__", "-D__WINESRC__",
"-D_M_IX86" if x86 else "-D_M_AMD64", "-I" + str(msi_root), "-i", msi_root / "msi.rc", "-o", objects / "msi.res.o"])
msi.append(objects / "msi.res.o")
msi.append(objects / "installer_support.o")
msi.append(compile(support / "clicktorun.c", "msi_clicktorun"))
msi.append(compile(source / "sdk/lib/3rdparty/libwine/dll_register.c", "msi_dll_register", ["-I" + str(msi_root)]))
link("msi.dll", bases["msi"], [*msi, *debug], output / "msi.def",
["-L" + str(output), "-lsxs", "-lcabinet", "-lrpcrt4", *[f"-l{library}" for library in ("advapi32", "comctl32", "gdi32", "ole32", "oleaut32", "shell32",
"shlwapi", "urlmon", "user32", "version", "wininet", "imagehlp", "msvcrt", "kernel32", "ntdll", "odbccp32", "crypt32",
"wintrust", "uuid", "gcc")]])
# raven-storage.dll: Raven's own structured storage, which the runtime's ole32 hands those exports to.
own_storage = repo / "native/upstream/raven-storage"
own_objects = []
for path in [*sorted(own_storage.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("storage_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=c11", "-fno-builtin", "-D_WIN32_WINNT=0x600", "-DUNICODE", "-D_OLE32_", "-DCONST_VTABLE",
"-I" + str(own_storage), path, "-o", target])
own_objects.append(target)
exported = (own_storage / "exports.txt").read_text().split()
defined = decorated(own_objects)
(output / "raven-storage.def").write_text("NAME raven-storage.dll\nEXPORTS\n" + "".join(
" " + name + ("@" + str(defined[name]) if x86 and defined[name] is not None else "") + "\n" for name in exported))
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{bases['storage']:#x},--no-insert-timestamp", *link_extra,
"-o", output / "raven-storage.dll", *own_objects, output / "raven-storage.def", "-lole32", "-loleaut32", "-luser32", "-ladvapi32", "-lkernel32", "-luuid", "-lgcc"])
# msiexec.exe: the command line of the installer, which installers and the shell start for packages.
msiexec_root = source / "base/system/msiexec"
msiexec = [compile(msiexec_root / name, "msiexec_" + Path(name).stem, ["-I" + str(msiexec_root)], wine_module=False)
for name in ("msiexec.c", "service.c")]
msiexec.append(compile(support / "entry.c", "msiexec_entry"))
run([triple + "-windres", "--preprocessor=" + str(wrapper), "-O", "coff", "-DLANGUAGE_EN_US", "-D__REACTOS__",
"-D_M_IX86" if x86 else "-D_M_AMD64", "-I" + str(msiexec_root), "-i", msiexec_root / "rsrc.rc", "-o", objects / "msiexec.res.o"])
run([triple + "-gcc", "-nostdlib", "-mwindows", f"-Wl,--entry,{'_raven_program_entry@0' if x86 else 'raven_program_entry'},--no-insert-timestamp",
"-o", output / "msiexec.exe", *msiexec, objects / "msiexec.res.o", objects / "installer_support.o", *debug, "-L" + str(output),
*runtime(f"-l{library}" for library in ("msi", "user32", "advapi32", "ole32", "comctl32", "msvcrt", "kernel32", "ntdll", "uuid", "gcc"))])
# msxml3.dll: the XML document object model installers and their bootstrappers parse their manifests with, on libxml2 and libxslt.
xml_root = source / "sdk/lib/3rdparty/libxml2"
iconv_root = source / "sdk/lib/3rdparty/libwin-iconv"
xml_flags = ["-U__WINESRC__", "-U_WINE", "-D_CRT_DECLARE_NONSTDC_NAMES=1", "-D__MINGW32__", "-D_DLL", "-D__USE_CRTIMP", "-DLIBXML_STATIC", "-D_REENTRANT", "-D_MBCS", "-D_WINDOWS", "-DWIN32", "-DHAVE_CONFIG_H", "-DICONV_CONST=const",
"-I" + str(xml_root / "include"), "-I" + str(xml_root), "-I" + str(iconv_root), "-I" + str(zlib_include)]
# The C runtime's mathematics, which mingw's msvcrt import library would bring in as static code of its own.
(output / "crtmath.def").write_text("LIBRARY msvcrt.dll\nEXPORTS\n" + "".join(" " + name + "\n" for name in
("floor", "ceil", "fabs", "fmod", "log", "log10", "exp", "pow", "sqrt", "atan", "atan2", "tan",
"floorf", "ceilf", "_hypot")))
run([triple + "-dlltool", "-d", output / "crtmath.def", "-l", output / "libcrtmath.a"])
xml = [compile(iconv_root / "win_iconv.c", "iconv_win_iconv", xml_flags, wine_module=False)]
xml_sources = sorted(path for path in xml_root.glob("*.c"))
with ThreadPoolExecutor(max_workers=2) as pool:
xml += list(pool.map(lambda path: compile(path, "libxml2_" + path.stem, xml_flags, wine_module=False), xml_sources))
xslt_root = source / "dll/3rdparty/libxslt"
xslt_flags = [*xml_flags, "-DHAVE_WIN32_THREADS", "-I" + str(xslt_root), "-I" + str(source / "sdk/include/reactos/libs/libxslt"), "-I" + str(source / "sdk/include/reactos/libs")]
with ThreadPoolExecutor(max_workers=2) as pool:
xslt = list(pool.map(lambda path: compile(path, "libxslt_" + path.stem, xslt_flags, wine_module=False), sorted(xslt_root.glob("*.c"))))
# Its registration scripts' type library comes from the public headers, and stays with stdole2's for what else needs it.
run([*widl, "-t", "-T", generated / "stdole2.tlb", repo / "native/upstream/raven-oleaut32/stdole2.idl"])
own_msxml = repo / "native/upstream/raven-msxml3"
headers = Path("/usr") / triple / "include"
run([sys.executable, own_msxml / "make_idl.py", headers / "msxml2.h", headers / "msxml2did.h", headers / "msxml.h", generated / "msxml3.idl"])
run([*widl, "-I", headers, "-L", generated, "-t", "-o", generated / "msxml3.tlb", generated / "msxml3.idl"])
run([triple + "-windres", "-O", "coff", "-I", generated, "-i", own_msxml / "resources.rc", "-o", objects / "msxml3_resources.o"])
msxml_flags = ["-O2", "-std=gnu11", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-DLIBXML_STATIC", "-DLIBXSLT_STATIC", "-DCONST_VTABLE", "-I" + str(own_msxml),
"-I" + str(xml_root / "include"), "-I" + str(xml_root), "-I" + str(source / "sdk/include/reactos/libs")]
msxml_objects = [objects / "msxml3_resources.o"]
for path in sorted(own_msxml.glob("*.c")):
target = objects / ("msxml3_" + path.stem + ".o")
run([triple + "-gcc", "-c", *msxml_flags, path, "-o", target])
msxml_objects.append(target)
(output / "msxml3.def").write_text("LIBRARY msxml3.dll\nEXPORTS\n" + (own_msxml / "exports.txt").read_text())
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{bases['msxml']:#x},--no-insert-timestamp,--allow-multiple-definition", *link_extra,
"-o", output / "msxml3.dll", *msxml_objects, *xslt, *xml, *zlib, objects / "installer_support.o", *debug, output / "msxml3.def", output / "libcrtmath.a",
*[f"-l{library}" for library in ("urlmon", "ws2_32", "shlwapi", "oleaut32", "ole32", "user32", "advapi32", "msvcrt", "kernel32", "ntdll", "uuid", "gcc", "kernel32", "msvcrt")]])
# atl100.dll: the ATL registrar, which reads the registration scripts libraries register their classes with, and the control host.
own_atl = repo / "native/upstream/raven-atl100"
atl_objects = []
for part in ("module", "registrar", "ax"):
atl_objects.append(objects / ("atl100_" + part + ".o"))
run([triple + "-gcc", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-DCONST_VTABLE", "-D_CRTIMP=", "-w", "-c", "-o", atl_objects[-1], own_atl / (part + ".c")])
(output / "atl100.def").write_text("LIBRARY atl100.dll\nEXPORTS\n" + (own_atl / "exports.txt").read_text())
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{bases['atl']:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "atl100.dll", *atl_objects, repo / "native/upstream/raven-comctl32/crt.c", output / "atl100.def", "-luuid", "-lole32", "-loleaut32", "-ladvapi32", "-lgdi32", "-luser32", "-lkernel32", "-lgcc"])
# oleaut32.dll: Raven's own automation, variants, safe arrays and type libraries, carrying the standard type library.
own_oleaut = repo / "native/upstream/raven-oleaut32"
oleaut_objects = []
for path in [*sorted(own_oleaut.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("oleaut32_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-D_OLEAUT32_", "-DCONST_VTABLE", "-D_CRTIMP=", "-I" + str(own_oleaut), path, "-o", target])
oleaut_objects.append(target)
run([triple + "-windres", "-I", generated, "-I", own_oleaut, own_oleaut / "resources.rc", "-o", objects / "oleaut32.res.o"])
oleaut_objects.append(objects / "oleaut32.res.o")
oleaut_defined = decorated(oleaut_objects)
def oleaut_export(line):
entry, _, fixed = line.partition(" ")
name, _, implementation = entry.partition("=")
target = implementation or name
words = "@" + str(oleaut_defined[target]) if x86 and oleaut_defined.get(target) is not None else ""
return f" {name}={implementation}{words} {fixed}\n" if implementation else f" {name}{words} {fixed}\n"
(output / "oleaut32.def").write_text("LIBRARY oleaut32.dll\nEXPORTS\n" + "".join(oleaut_export(line) for line in (own_oleaut / "exports.txt").read_text().splitlines()))
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{bases['oleaut']:#x},--no-insert-timestamp", *link_extra,
"-o", output / "oleaut32.dll", *oleaut_objects, output / "oleaut32.def", "-luuid", "-lole32", "-lgdi32", "-luser32", "-ladvapi32", "-lkernel32", "-lgcc"])
# The libraries below follow one pattern: the sources their ReactOS build lists, compiled with its definitions, a definition file from
# their specification and the libraries they import. Their base addresses count down from below the ones above.
next_base = [0x1c4000000 if not x86 else 0x1c400000]
def listed_sources(root):
text = (root / "CMakeLists.txt").read_text()
names = []
for block in re.findall(r"list\(APPEND SOURCE(.*?)\)", text, re.S)[:1]:
names += [token for token in block.split() if token.endswith(".c") and not token.startswith(("#", "$"))]
return [root / name for name in names]
def library(name, root, extra_flags=(), libraries=(), wine_module=True, sources=None, objects_more=(), helpers=(), rc=None, interfaces=()):
stem = Path(name).stem
spec = root / (stem + ".spec") if (root / (stem + ".spec")).exists() else next(root.glob("*.spec"))
run([output / "spec2def", spec_arch, "-n=" + name, "-d=" + str(output / (stem + ".def")), "-s=" + str(generated / (stem + "_stubs.c")), spec])
flags_here = [*extra_flags, "-I" + str(root)]
parts = sources if sources is not None else listed_sources(root)
with ThreadPoolExecutor(max_workers=2) as pool:
built = list(pool.map(lambda path: compile(path, stem + "_" + path.stem.replace(".", "_"), flags_here, wine_module), parts))
built.append(compile(generated / (stem + "_stubs.c"), stem + "_spec_stubs", flags_here, wine_module))
# The identifiers of the public interfaces it names, which mingw-w64's uuid library does not all have.
for interface in interfaces:
run([*widl, "-u", "-U", generated / (interface + "_i.c"), source / "sdk/include/psdk" / (interface + ".idl")])
built.append(compile(generated / (interface + "_i.c"), stem + "_" + interface + "_i", flags_here, wine_module))
for helper in helpers:
built.append(compile(source / "sdk/lib/3rdparty/libwine" / helper, stem + "_" + Path(helper).stem, flags_here))
if rc:
run([triple + "-windres", "--preprocessor=" + str(wrapper), "-O", "coff", "-DLANGUAGE_EN_US", "-D__REACTOS__", "-D__WINESRC__",
"-D_M_IX86" if x86 else "-D_M_AMD64", "-I" + str(root), "-I" + str(generated), "-i", root / rc, "-o", objects / (stem + ".res.o")])
built.append(objects / (stem + ".res.o"))
next_base[0] -= 0x400000 if x86 else 0x4000000
link(name, next_base[0], [*built, *objects_more, objects / "installer_support.o", *debug], output / (stem + ".def"),
["-L" + str(output), "-lcrtmath", *[f"-l{library}" for library in (*libraries, "msvcrt", "kernel32", "ntdll", "uuid", "gcc", "kernel32", "msvcrt")]],
output / ("lib" + stem + ".a"))
plain = ["-U__WINESRC__", "-U_WINE", "-D_CRT_DECLARE_NONSTDC_NAMES=1", "-D_DLL", "-DWIN32", "-D_WINDOWS"]
entry_object = compile(support / "dllmain.c", "plain_dllmain")
libs = source / "sdk/include/reactos/libs"
library("libpng.dll", source / "dll/3rdparty/libpng", [*plain, "-DNDEBUG=", "-DPNG_BUILD_DLL", "-I" + str(libs / "libpng"), "-I" + str(zlib_include)],
wine_module=False, objects_more=[*zlib, entry_object])
library("libjpeg.dll", source / "dll/3rdparty/libjpeg", [*plain, "-D_MBCS", "-DJPEG_DLL", "-Dmain=mainptr", "-I" + str(libs / "libjpeg")], wine_module=False,
objects_more=[entry_object])
library("libtiff.dll", source / "dll/3rdparty/libtiff", [*plain, "-DNDEBUG=", "-DUSE_WIN32_FILEIO", "-DTIF_PLATFORM_CONSOLE", "-I" + str(libs / "libtiff"),
"-I" + str(libs / "libjpeg"), "-I" + str(zlib_include)], ["libjpeg", "user32"], wine_module=False, objects_more=[*zlib, entry_object])
registration = ("register.c", "dll_register.c", "dll_canunload.c")
# propsys.dll: property values, the in-memory store and the system's property table, generated from the public key list.
own_propsys = repo / "native/upstream/raven-propsys"
propsys_flags = ["-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-DCONST_VTABLE", "-DINITGUID", "-D_PROPSYS_", "-I" + str(generated)]
run([sys.executable, own_propsys / "make_schema.py", Path("/usr/share/mingw-w64/include/propkey.h"), generated / "schema.inc"])
propsys_objects = []
for part in ("convert", "store", "system", "main"):
propsys_objects.append(objects / ("propsys_" + part + ".o"))
run([triple + "-gcc", *propsys_flags, "-w", "-c", "-o", propsys_objects[-1], own_propsys / (part + ".c")])
(output / "propsys.def").write_text("LIBRARY propsys.dll\nEXPORTS\n" + (own_propsys / "exports.txt").read_text())
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "propsys.dll", *propsys_objects, repo / "native/upstream/raven-comctl32/crt.c", output / "propsys.def", "-luuid", "-lole32", "-loleaut32", "-ladvapi32", "-luser32", "-lkernel32", "-lgcc"])
# Other components link against the callers' view: on x86 the names carry their argument sizes, which only the objects know.
wanted = {line.split("=")[0].strip() for line in (own_propsys / "exports.txt").read_text().splitlines() if "=" not in line}
propsys_names = sorted({word.lstrip("_") for line in subprocess.run([triple + "-nm", "-g", "--defined-only", *map(str, propsys_objects)], env=env, check=True, capture_output=True, text=True).stdout.splitlines()
for word in line.split()[-1:] if word.lstrip("_").split("@")[0] in wanted})
(output / "propsys_import.def").write_text("LIBRARY propsys.dll\nEXPORTS\n" + "".join(f" {name}\n" for name in propsys_names))
run([triple + "-dlltool", *(["-k"] if x86 else []), "-d", output / "propsys_import.def", "-l", output / "libpropsys.a"])
# mlang.dll: code pages, language tags and font linking.
own_mlang = repo / "native/upstream/raven-mlang"
mlang_objects = []
for part in ("pages", "tags", "fontlink", "language", "main"):
mlang_objects.append(objects / ("mlang_" + part + ".o"))
run([triple + "-gcc", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-DCONST_VTABLE", "-w", "-c", "-o", mlang_objects[-1], own_mlang / (part + ".c")])
(output / "mlang.def").write_text("LIBRARY mlang.dll\nEXPORTS\n" + (own_mlang / "exports.txt").read_text())
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "mlang.dll", *mlang_objects, repo / "native/upstream/raven-comctl32/crt.c", output / "mlang.def", "-luuid", "-lole32", "-loleaut32", "-ladvapi32", "-lgdi32", "-luser32", "-lkernel32", "-lgcc"])
wanted = {line.split()[0] for line in (own_mlang / "exports.txt").read_text().splitlines() if "PRIVATE" not in line}
mlang_names = sorted({word.lstrip("_") for line in subprocess.run([triple + "-nm", "-g", "--defined-only", *map(str, mlang_objects)], env=env, check=True, capture_output=True, text=True).stdout.splitlines()
for word in line.split()[-1:] if word.lstrip("_").split("@")[0] in wanted})
(output / "mlang_import.def").write_text("LIBRARY mlang.dll\nEXPORTS\n" + "".join(f" {name}\n" for name in mlang_names))
run([triple + "-dlltool", *(["-k"] if x86 else []), "-d", output / "mlang_import.def", "-l", output / "libmlang.a"])
# windowscodecs.dll: Raven's own imaging component; JPEG and TIFF go through the libraries built above, named by path so
# no export of the component can resolve to one of the installer's own import libraries.
own_codecs = repo / "native/upstream/raven-windowscodecs"
codecs_objects = []
for path in [*sorted(own_codecs.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("codecs_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-D_CRTIMP=", "-DCOBJMACROS", "-I" + str(own_codecs),
"-I" + str(libs / "libjpeg"), "-I" + str(libs / "libtiff"), "-w", path, "-o", target])
codecs_objects.append(target)
codecs_exports = (own_codecs / "exports.txt").read_text()
(output / "windowscodecs.def").write_text("LIBRARY windowscodecs.dll\nEXPORTS\n" + codecs_exports)
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "windowscodecs.dll", *codecs_objects, output / "windowscodecs.def", output / "liblibjpeg.a", output / "liblibtiff.a", "-lole32", "-loleaut32", "-luuid", "-ladvapi32",
"-lgdi32", "-luser32", "-lkernel32", "-lgcc"])
codecs_defined = decorated(codecs_objects)
codecs_names = [line.split()[0].partition("=")[0] for line in codecs_exports.splitlines() if line.strip()]
(output / "windowscodecs_import.def").write_text("LIBRARY windowscodecs.dll\nEXPORTS\n" + "".join(
f" {name}{'@' + str(codecs_defined[name]) if x86 and codecs_defined.get(name) is not None else ''}\n" for name in codecs_names))
run([triple + "-dlltool", *(["-k"] if x86 else []), "-d", output / "windowscodecs_import.def", "-l", output / "libwindowscodecs.a"])
# gdiplus.dll: Raven's own drawing library, which reads and writes pictures through the imaging component.
own_gdiplus = repo / "native/upstream/raven-gdiplus"
gdiplus_objects = []
for path in [*sorted(own_gdiplus.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("gdiplus_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-D_UNICODE", "-DCOBJMACROS", "-D_CRTIMP=", "-I" + str(own_gdiplus), path, "-o", target])
gdiplus_objects.append(target)
gdiplus_exports = (own_gdiplus / "exports.txt").read_text()
(output / "gdiplus.def").write_text("LIBRARY gdiplus.dll\nEXPORTS\n" + gdiplus_exports)
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "gdiplus.dll", *gdiplus_objects, output / "gdiplus.def", "-lole32", "-loleaut32", "-lshlwapi", "-luuid", "-luser32", "-lgdi32", "-lkernel32", "-ladvapi32", "-lgcc"])
gdiplus_defined = decorated(gdiplus_objects)
gdiplus_names = [line.strip() for line in gdiplus_exports.splitlines() if line.strip()]
(output / "gdiplus_import.def").write_text("LIBRARY gdiplus.dll\nEXPORTS\n" + "".join(
f" {name}{'@' + str(gdiplus_defined[name]) if x86 and gdiplus_defined.get(name) is not None else ''}\n" for name in gdiplus_names))
run([triple + "-dlltool", *(["-k"] if x86 else []), "-d", output / "gdiplus_import.def", "-l", output / "libgdiplus.a"])
# urlmon.dll: Raven's own. Ordinals without a name are the ones old callers import by.
own_urlmon = repo / "native/upstream/raven-urlmon"
urlmon_objects = []
for path in [*sorted(own_urlmon.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("urlmon_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-DCONST_VTABLE", "-D_CRTIMP=", "-I" + str(own_urlmon), path, "-o", target])
urlmon_objects.append(target)
urlmon_defined = decorated(urlmon_objects)
def urlmon_export(line):
entry, _, fixed = line.partition(" ")
name, _, implementation = entry.partition("=")
target = implementation or name
words = "@" + str(urlmon_defined[target]) if x86 and urlmon_defined.get(target) is not None else ""
if name.startswith("@"):
return f" ordinal{name[1:]}={target}{words} {name} NONAME\n"
return f" {name}={implementation}{words} {fixed}\n" if implementation else f" {name}{words} {fixed}\n"
(output / "urlmon.def").write_text("LIBRARY urlmon.dll\nEXPORTS\n" + "".join(urlmon_export(line) for line in (own_urlmon / "exports.txt").read_text().splitlines()))
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp", *link_extra,
"-o", output / "urlmon.dll", *urlmon_objects, output / "urlmon.def", "-luuid", "-lole32", "-loleaut32", "-lshlwapi", "-ladvapi32", "-lwininet", "-luser32", "-lshell32", "-lkernel32", "-lgcc"])
# riched20.dll: Raven's own rich edit control, with the type library of its text object model that its resources carry.
own_riched = repo / "native/upstream/raven-riched20"
run([*widl, "-t", "-T", generated / "tom_library.tlb", own_riched / "tom_library.idl"])
run([triple + "-windres", "-I", generated, own_riched / "resources.rc", "-o", objects / "riched20.res.o"])
riched_objects = [objects / "riched20.res.o"]
for path in [*sorted(own_riched.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("riched20_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-D_UNICODE", "-DCOBJMACROS", "-D_CRTIMP=", "-I" + str(own_riched), path, "-o", target])
riched_objects.append(target)
riched_exports = (own_riched / "exports.txt").read_text()
(output / "riched20.def").write_text("LIBRARY riched20.dll\nEXPORTS\n" + riched_exports)
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "riched20.dll", *riched_objects, output / "riched20.def", "-lole32", "-loleaut32", "-luuid", "-luser32", "-lgdi32", "-lkernel32", "-lgcc"])
(output / "riched20_import.def").write_text("LIBRARY riched20.dll\nEXPORTS\n" + (" CreateTextServices@12\n" if x86 else " CreateTextServices\n"))
run([triple + "-dlltool", *(["-k"] if x86 else []), "-d", output / "riched20_import.def", "-l", output / "libriched20.a"])
# msftedit.dll: the newer rich edit library, which imports riched20.dll and passes every export on to it.
(output / "msftedit.def").write_text("LIBRARY msftedit.dll\nEXPORTS\n" + "".join(
f"\t{name} = riched20.{name}{' DATA' if name.startswith('IID_') else ''}\n" for name in ("CreateTextServices", "IID_IRichEditOle",
"IID_IRichEditOleCallback", "IID_ITextHost", "IID_ITextHost2", "IID_ITextServices", "REExtendedRegisterClass", "RichEdit10ANSIWndProc",
"RichEditANSIWndProc")))
next_base[0] -= 0x400000 if x86 else 0x4000000
link("msftedit.dll", next_base[0], [compile(support / "msftedit.c", "msftedit", plain, False)], output / "msftedit.def",
["-L" + str(output), *[f"-l{library}" for library in ("riched20", "kernel32", "gcc")]])
# msvfw32.dll: Raven's own video for Windows: the codec manager, DrawDib and the MCI window.
own_vfw = repo / "native/upstream/raven-msvfw32"
vfw_objects = []
for part in ("codecs", "draw", "mciwnd", "main"):
vfw_objects.append(objects / ("msvfw32_" + part + ".o"))
run([triple + "-gcc", "-O2", "-std=gnu11", "-fno-builtin", "-D_WIN32_WINNT=0x601", "-DUNICODE", "-D_CRTIMP=", "-w", "-c", "-o", vfw_objects[-1], own_vfw / (part + ".c")])
(output / "msvfw32.def").write_text("LIBRARY msvfw32.dll\nEXPORTS\n" + (own_vfw / "exports.txt").read_text())
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp,--enable-stdcall-fixup", *link_extra,
"-o", output / "msvfw32.dll", *vfw_objects, repo / "native/upstream/raven-comctl32/crt.c", output / "msvfw32.def", "-lcomdlg32", "-ladvapi32", "-lgdi32", "-luser32", "-lkernel32", "-lgcc"])
# xmllite.dll: the streaming XML reader and writer, which the Office Click-to-Run client reads its configuration with.
xmllite_root = repo / "native/upstream/raven-xmllite"
library("xmllite.dll", xmllite_root, wine_module=False, sources=[xmllite_root / name for name in ("guid.c", "reader.c", "writer.c")])
# raven-setupapi.dll: Raven's own setup API, which the runtime's setupapi.dll hands those exports to.
own_setup = repo / "native/upstream/raven-setupapi"
setup_objects = []
for path in [*sorted(own_setup.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("setup_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=c11", "-fno-builtin", "-D_WIN32_WINNT=0x600", "-DUNICODE", "-D_SETUPAPI_", "-D_SETUPAPI_VER=0x0600", "-DCONST_VTABLE",
"-I" + str(own_setup), path, "-o", target])
setup_objects.append(target)
defined = decorated(setup_objects)
def setup_export(entry):
name, _, implementation = entry.partition("=")
words = "@" + str(defined[implementation or name]) if x86 and defined[implementation or name] is not None else ""
return f" {name}={implementation}{words}\n" if implementation else f" {name}{words}\n"
(output / "raven-setupapi.def").write_text("LIBRARY raven-setupapi.dll\nEXPORTS\n" + "".join(setup_export(entry) for entry in (own_setup / "exports.txt").read_text().split()))
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp", *link_extra,
"-o", output / "raven-setupapi.dll", *setup_objects, output / "raven-setupapi.def", "-lole32", "-lshell32", "-lversion", "-ladvapi32", "-luser32", "-lkernel32", "-luuid", "-lgcc"])
# advpack.dll: Raven's own, over that setup API. Its export order gives the ordinals old callers import by.
own_advpack = repo / "native/upstream/raven-advpack"
advpack_objects = []
for path in [*sorted(own_advpack.glob("*.c")), repo / "native/upstream/raven-comctl32/crt.c"]:
target = objects / ("advpack_" + path.stem + ".o")
run([triple + "-gcc", "-c", "-O2", "-std=c11", "-fno-builtin", "-D_WIN32_WINNT=0x600", "-DUNICODE", "-I" + str(own_advpack), path, "-o", target])
advpack_objects.append(target)
defined = decorated(advpack_objects)
def advpack_export(ordinal, entry):
name, _, implementation = entry.partition("=")
words = "@" + str(defined[implementation or name]) if x86 and defined[implementation or name] is not None else ""
return f" {name}={implementation}{words} @{ordinal}\n" if implementation else f" {name}{words} @{ordinal}\n"
(output / "advpack.def").write_text("LIBRARY advpack.dll\nEXPORTS\n" + "".join(advpack_export(ordinal, entry) for ordinal, entry in enumerate((own_advpack / "exports.txt").read_text().split(), 1)))
next_base[0] -= 0x400000 if x86 else 0x4000000
run([triple + "-gcc", "-shared", "-nostdlib", f"-Wl,--entry,{'_DllMain@12' if x86 else 'DllMain'},--image-base,{next_base[0]:#x},--no-insert-timestamp", *link_extra,
"-o", output / "advpack.dll", *advpack_objects, output / "advpack.def", "-lsetupapi", "-lole32", "-lshell32", "-lversion", "-ladvapi32", "-luser32", "-lkernel32", "-luuid", "-lgcc"])
result = dict(revision=REVISION, archive_sha256=ARCHIVE_SHA256, arch=args.arch, commands=records,
license_notices=preserve_notices(source, output),
component_sha256={name: hashlib.sha256((output / name).read_bytes()).hexdigest() for name in COMPONENTS},
storage_exports=exported,
patches={patch.name: hashlib.sha256(patch.read_bytes()).hexdigest() for patch in patches},
source_hashes={path.relative_to(source).as_posix(): hashlib.sha256(path.read_bytes()).hexdigest()
for path in sorted(source.rglob("*")) if path.is_file() and not path.is_relative_to(overlay)})
(output / "provenance.json").write_text(json.dumps(result, indent=2) + "\n")
for name in COMPONENTS:
print(output / name)
if __name__ == "__main__":
main()