mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-01 21:45:06 +00:00
588 lines
49 KiB
Python
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()
|