A first foray into reproducible builds
Description
Makes Windows builds mostly reproducible using undocumented (but working) MSVC compiler/linker options and dropping the Qt Resource Compiler version to 1 (since 2+ includes last-modified timestamps).
PDBs won't be identical due to lambdas having random identifiers but they are compatible, meaning that it would be possible to also create debug symbols for any build after the fact.
ToDo:
- [x] Figure out how to make cmake behave when it comes to escaping the
/PDBALTPATHlinker option- CMake bug: https://gitlab.kitware.com/cmake/cmake/-/issues/15865 (7 years old D:)
- Possible workaround: https://github.com/boostorg/stacktrace/issues/55#issuecomment-984782636
- [ ] Figure out why
coreaudio-encoderis not currently reproducible - [ ] (Maybe) macOS
Motivation and Context
We eventually want to have fully reproducible builds for OBS and its dependencies. For the reason why you may want that, see https://reproducible-builds.org/docs/buy-in/
Aside from the security implications and verifiability, a side-benefit of reproducible builds would be that delta patches as we employ on Windows (and possibly macOS in the future) would be smaller, and determining which files haven't changed to exclude them from an update could be more easily automated. (Note: Ensuring that timestamps are always the same even for adjacent builds ducible may still be a required, it can be added to CMake for example like so: https://github.com/derrod/obs-studio/commit/5f4dee7423f50ea47f073831fdd7165cd1d10e67)
Getting OBS itself into a state where building it with our precompiled Windows and macOS dependencies will create identical binaries is a first step.
How Has This Been Tested?
Ran various tests on my fork, for the same commit, all binaries except obs-websocket and coreaudio-encoder generate identical files (sha512 checked).
A workflow run with only two x64 Windows jobs that have produced mostly-identical builds is here: https://github.com/derrod/obs-studio/actions/runs/4099136967
Types of changes
- Tweak (non-breaking change to improve existing functionality)
Checklist:
- [x] My code has been run through clang-format.
- [x] I have read the contributing document.
- [x] My code is not on the master branch.
- [x] The code has been tested.
- [x] All commit messages are properly formatted and commits squashed where appropriate.
- [x] I have included updates to all appropriate documentation.
Use _QT_VERSION not QT_VERSION, the latter is set to AUTO. _QT_VERSION is set to 5 or 6.
Went back to just setting the AUTORCC options for now, and made it limited to Windows. Reproducible builds aren't really a thing on macOS and the main motivation of this PR is to make Windows builds reproducible. Linux is a problem for a different time.
I'd still like to incorporate https://github.com/jasonwhite/ducible to ensure that EXE/PDB metadata is always consistent (which also reduces delta update sizes!) but I would probably have to add it to obs-deps first.
(The inconsistencies with coreaudio are not yet figured out, I'll have to get back to that. But I have to wonder if #8377 would actually help with that?)
Indeed #8377 seems to have fixed coreaudio-encoder.dll not being reproducible. I will need to also submit the Qt RCC options to obs-websocket for our CI builds to be fully reproducible.
I will consider this PR ready for review now.
The workflow run here can now be checked to see the results of this PR with two parallel builds: https://github.com/obsproject/obs-studio/actions/runs/4314253892
When checking the hashes, only the PDBs and obs-websocket.dll are mismatched, the former is not an issue, the latter will require the PR to the websockets repo to be merged: https://github.com/obsproject/obs-websocket/pull/1107
I think I commented off-thread about a concern with forcing Qt RCC to v1 and wanting to know if this was a known deficiency to Qt (i.e., in a QTBUG) with v2. Ideally, we'd be able to use v2 (or some later v3) without having to keep ourselves stuck on this older format.
v2 introduces last-modified timestamps, so this isn't a bug, but a known problem for reproducability. Qt 5.11 introduced an environment variable (QT_RCC_SOURCE_DATE_OVERRIDE) to set a fixed timestamp, but you cannot set it from the rcc command line parameters and setting it from CMake has not worked for me.
There is no benefit to using v2 or v3 for us. v2 introduces timestamps, v3 zstd compression which we do not build. If we ever do want to use a newer version we'll have to either:
- Somehow set
SOURCE_DATE_EPOCH/QT_RCC_SOURCE_DATE_OVERRIDEbefore runningrcc - Modify the last-modified timestamp of all resource files to a fixed value before running
rcc - Patch our obs-deps
rccto have a fixed timestamp
I think I commented off-thread about a concern with forcing Qt RCC to v1 and wanting to know if this was a known deficiency to Qt (i.e., in a QTBUG) with v2. Ideally, we'd be able to use v2 (or some later v3) without having to keep ourselves stuck on this older format.
v2 introduces last-modified timestamps, so this isn't a bug, but a known problem for reproducability. Qt 5.11 introduced an environment variable (
QT_RCC_SOURCE_DATE_OVERRIDE) to set a fixed timestamp, but you cannot set it from thercccommand line parameters and setting it from CMake has not worked for me.There is no benefit to using v2 or v3 for us. v2 introduces timestamps, v3 zstd compression which we do not build. If we ever do want to use a newer version we'll have to either:
* Somehow set `SOURCE_DATE_EPOCH`/`QT_RCC_SOURCE_DATE_OVERRIDE` before running `rcc` * Modify the last-modified timestamp of all resource files to a fixed value before running `rcc` * Patch our obs-deps `rcc` to have a fixed timestamp
Could we not set the environment variable early via CMake? I suppose it is not something that needs addressed now, I'm just wary of pinning on an old version causing problems for us down the road.
If each commit builds on its own, then no need to further squash.
Could we not set the environment variable early via CMake? I suppose it is not something that needs addressed now, I'm just wary of pinning on an old version causing problems for us down the road.
I've tried that and it didn't work (at least on Windows). We can revisit if there are any changes on Qt's side that make using the old version untenable for us. See their note (emphasis mine):
This command affects only the current CMake process, not the process from which CMake was called, nor the system environment at large, nor the environment of subsequent build or test processes.
If each commit builds on its own, then no need to further squash.
They all build on their own.
I've tried that and it didn't work (at least on Windows). We can revisit if there are any changes on Qt's side that make using the old version untenable for us. See their note (emphasis mine):
This command affects only the current CMake process, not the process from which CMake was called, nor the system environment at large, nor the environment of subsequent build or test processes.
Ah. I see. That's curious, if not a bit unfortunate. We can, of course, set this in CI environments elsewhere. To set it in a non-CI environment, we could probably set it from a build script, or else we would have to set it manually (yuck).
As an aside, QTBUG-105926 seems to be the Qt place to track work on reproducible builds.
Alas there is no support for any environment variables in Qt's CMake rcc modules, so there is no way to influence this behaviour with them (which makes sense given that Qt doesn't consider reproducible builds "supported" and they haven't considered support for it yet).
The only other way would be to set it explicitly in the environment before running cmake.