Setup Krita builds for Windows on the Binary Factory
Closed, ResolvedPublic

Description

Heya,

we have a new job on Binary Factory to run arbitrary binaries and zip files through Windows' sign tool:
https://binary-factory.kde.org/job/signme_upload_job/

We'd like to limit access to this job as much as possible. Right now only sysadmins and me may upload binaries to this job and let them be signed.

At least Boud should get access, too, to be able to sign his Krita binaries. I don't want this job to be glued to me alone. I think it's okay to have a circle of trusted people who can freely use the signtool.

kfunk created this task.Sep 25 2017, 9:18 AM
Restricted Application added a subscriber: sysadmin. · View Herald TranscriptSep 25 2017, 9:18 AM
kfunk updated the task description. (Show Details)Sep 25 2017, 9:18 AM
kfunk renamed this task from Give Boud permissions to trigger jobs on Binary Factory for code signing Krita artifacts to Give some people permissions to trigger jobs on Binary Factory for code signing arbitrary binaries.

Couple of notes from my perspective:

  1. This should be a transition task only, until Boud can get his process running on the Binary Factory systems
  2. As far as i'm aware everyone else uses Craft, so they can just go through the regular Binary Factory process.

Thoughts Boud?

rempt added a comment.EditedSep 25 2017, 9:51 AM

Since 2013 or so, I've tried to use craft/emerge a bunch of times, and it never worked out for me. I've never ever had a single problem-free run, either because upstream moved tarballs, or because of updates that break Krita or just random breakage.

We also want to build Krita with a well-known set of dependencies, with our patches, where the version of the dependencies only changes when we explicitly want them too, not because there's a new release.

As I've indicated before, I just don't see us use craft in the near future. It should be possible to create a batch file that builds Krita and its deps in one go, though.

Sorry I should have been clearer that I don't intend to push Krita towards Craft.
Everyone else can use it of course as to my knowledge nobody else has their own tooling.

Um, I'm not exactly sure what it is that I should do next?

For my part: I think it's understandable that for some project maintainers, it's not feasible to use Craft (probably not even in future).

So I think we'll need a way to grant some people permanent permissions to triggering signing jobs through binary factory.

Regardless of the process they follow, I'd like to see the actual compilation process run on the Binary Factory. Whether they use Craft or something else doesn't matter.

Signing arbitrary executables is just asking for trouble. If we compile it on the Binary Factory then we at least have a full build log and know what sources were used to create those binaries.

To my knowledge Krita is the only one who has a custom (non-Craft) build process.

Boud: what do we need to do in order to get Krita building on the Binary Factory?

rempt added a comment.Sep 27 2017, 7:44 AM

I don't know much about the binary factory. Here's a couple of points for building Krita on Windows, though. Building Krita on Windows is pretty simple, even if the list of instructions is long...

  • We don't build from git; we build from a release tarball with translations included. We're using create_tarball_kf5.rb for that
  • Krita and the dependencies should be built using mingw 7.1
  • For building the dependencies, we use external cmake projects. The procedure is like this:
  • cmake expects the following directory layout: \d, \b, \i, \krita-x.x.x.
  • b is where externals are built
  • i is where everything gets installed
  • \krita-x.x.x is where the tarball is unpacked
  • All externals are built first; it would be good to be able to cache after this stage, because it takes a lot of time.
  • Then Krita is built
  • Then we package krita with a batchfile: we create two zips, one with stripped binaries, one with the debug symbols. The contents of the binary zip should be signed.
  • Then we create the setup.exe using nsis from the contents of the binary zip, and sign the setup.exe

It should be possible to put all of this in one or two batch scripts or powershell scripts, but I don't know powershell. Right now, I use four scripts in order: one for building the deps, one for building krita, one for packaging the zips, one for running nsis.

Jenkins has the ability to execute batch scripts so that should be possible to sort out.

I'd envision two jobs - the first to sort out the Dependencies, the second to do the Build, Zip packaging and NSIS installer creation.
The zips and installers could then be captured as artifacts much like how Kevin's current process does.

For maximum portability between builders the Dependencies themselves would be captured into an archive, with the second job using the most recent successful archive (Jenkins has a mechanism for handling this I think).

You'd then download the installers and zip files from binary-factory, test and upload to download.kde.org / files.kde.org like you normally do.

How does that sound?

rempt added a comment.Sep 27 2017, 8:11 AM

That should be perfect.

Kevin, is there anyone else that potentially needs to be sorted out here?

kfunk added a comment.Sep 27 2017, 8:35 AM

@bcooksley I don't think so.

But it feels like there's now even more reasons Boud needs to get access to the worker, so he can get this build running on the Windows VM. He might even need local access to the VM to debug build issues.

Next step: I'd need the public location of the build script so I can plug it into the Binary Factory and then give it a test run. After that we can check whether it 'just works' (it likely won't) and what next steps are to be taken.

Fingers crossed that the process works as expected - we'll have to see how it goes.
I think it's been run on more than Boud's system, so hopefully that will make things go more smoothly.

For build issues, those should be solved on the main CI system before they progress to the Binary Factory so those shouldn't be an issue.

rempt added a comment.Oct 9 2017, 12:14 PM

Is it possible to make this task visible? I would like to share it with the team.

rempt added a comment.Oct 9 2017, 12:14 PM

We'd first need the two build scripts -- one to setup and archive the dependencies, and one to fetch the deps, krita and build krita, then package and sign it -- we don't have anything that does that in one go.

If you can post what you have currently we can take a look at it.

I'll retitle and open this task shortly.

bcooksley renamed this task from Give some people permissions to trigger jobs on Binary Factory for code signing arbitrary binaries to Setup Krita builds for Windows on the Binary Factory.Oct 9 2017, 6:43 PM
bcooksley changed the visibility from "Custom Policy" to "Public (No Login Required)".
bcooksley changed the edit policy from "Custom Policy" to "All Users".

Hi, sorry it took me so long to comment on this task. I've made two scripts (which I have yet to document...) for compiling master branch and making the zip package, which I've pushed to the Krita git repo on the branch alvin/T7198-win-build-scripts. But making the installer is a bit convoluted (which I will touch on further down in this reply)...

The scripts can be called directly without having to copy them out of the source tree, but they can work when copied elsewhere too. The scripts supports being passed some command-line arguments, and some paths can be configured via environment variables. It can also build the dependencies as a separate step. There's a bit of a problem though: The master branch is for 4.0 and there are some slight differences in building and packaging so I'll also need to adapt the scripts for the branch krita/3.3 before a "release" build can be tested...


Some more points on building Krita:

Build environment requirements are listed in 3rdparty/readme.md in the source tree, but I'll restate them here:

  • CMake: A recent version, but it must be below 3.9, I am using 3.8.2
  • mingw-w64 gcc 7.1.0 from mingw-builds (posix threading model, SEH for 64-bit and dwarf for 32-bit)
  • Python: On branch krita/3.3 it's only for building Qt, but on master the full Python 3.6.x installs are needed for building Python scripting and the architecture of Python must match the build architecture (e.g. 64-bit Python 3.6.x when building 64-bit Krita master)
  • Windows 10 SDK for building libANGLE when building Qt, and for grabbing a copy of d3dcompiler redistributable DLL when making the package (which is done by windeployqt)
  • 7-zip for packaging

The simplest way is to just put CMake, mingw and Python on the front of PATH, Windows 10 SDK and 7-zip on the default install locations, and the scripts will find them.

I'm not sure if one can actually move the dependencies around after they're built and "installed". I think Qt specifically is a bit picky on where it's located. You can pack the dependencies into an archive and delete the originals but they probably will need to be extracted to the same path as their original install prefix (corresponding to --deps-install-dir for the scripts). This might be tricky in a Jenkins environment. (Though I could be wrong since I've only tried doing it once, over a year ago.)

There is also another issue that, since the source tarball is built on Linux, the patch files for the dependencies are in LF line endings, which will cause the patch tool we use on Windows to crash rather unpleasantly It isn't an issue with a git clone if core.autocrlf is set to true, but release builds need to use the translations from the source tarball.


I haven't decided how would be the best way to make the installers. Ideally it should be built with the zip package after the signing process, which would mean two extra steps in the whole process, and it might be a bit tricky.

Another tricky bit is that the NSIS scripts for building the Krita installer is currently placed on my git repo for the shell extension (https://github.com/alvinhochun/KritaShellExtension). I think what Boud does is to have the git cloned locally, download a binary release of the shell extension, and then build the installer with the scripts inside the git repo. And if I updated the shell extension and/or installer scripts I just ask Boud to update them. It's some annoying manual work.

(I would want to move the Krita installer scripts over to the Krita git repo, but then I'm not sure how to deal with the shell extension. The shell extension does not depend on any part of Krita and Krita does not directly use it so I want to keep it as a separate repo, but then the Krita installer script is tied to the shell extension installer scripts, and then the installer scripts are tied to the binary release DLLs. I still haven't been able to reach a conclusion on how to resolve this.)


So, in short, these are the things that need to be done before we can test making the build:

  • I need to adapt the scripts for a 3.3 build
  • A build environment needs to be set up
  • We need to figure out what to do with the dependencies. Might need some local experimenting...
  • (Not so important before a signed zip package can be build:) Find a way to allow the NSIS installer build to be automated

Sorry for this horribly long wall of text... there's too much I want to say here :S

I'll have a play with this tonight and see how I go.

rempt added a comment.Nov 4 2017, 9:17 AM

I think we could add the shell extension as binary artefacts to the Krita git repo, that would solve one problem.

I also wonder whether these scripts handle the installation of a binary gettext correctly -- this is another manual step for me, where I get the right gettext, and copy everything, except for the libc++ into the installation directory so cmake can find the msgfmt exe and run it.

How difficult is the shell extension to build? If it's not too hard, we could add that as a separate job.

If that is possible, then we can break the installer creation out into a separate job, which ingests two artifacts: the normal Krita zip file, and the shell extension (total of 4 jobs: Dependencies Build, Krita Build, Shell Extension Build, Installer Build)

How difficult is the shell extension to build? If it's not too hard, we could add that as a separate job.

If that is possible, then we can break the installer creation out into a separate job, which ingests two artifacts: the normal Krita zip file, and the shell extension (total of 4 jobs: Dependencies Build, Krita Build, Shell Extension Build, Installer Build)

They're built with VS2015 and pretty straightforward. It has some dependencies set with CMake ExternalProject and everything should just build smoothly. I might update to VS2017 next time I have to rebuild, and hopefully it will just work without any changes.

However, I don't quite like the idea of rebuilding the shell extension when making Krita releases. The reason is that if there are multiple builds of the same version and some users get a bug or a crash, it would be difficult to track the exact version of the shell extension installed. If the user provides a minidump (which tbh isn't very likely to happen), I will need to find the exact version of the debug symbols. (Though I don't have debug symbols for the current latest release, I will probably change the build to include debug symbols for the next release.) And it's a bit more complicated because I also make standalone installers for just the shell extension and they can also overwrite or upgrade the ones installed with Krita.

I actually have a builder on AppVeyor that builds the shell extension, but I don't use it for anything... I build the releases on my own system.

In T7064#116846, @rempt wrote:

I think we could add the shell extension as binary artefacts to the Krita git repo, that would solve one problem.

That would seem weird... maybe better as an ExternalProject which only downloads and extract the release, under a special optional target under the main project (not 3rdparty)?

This makes me think, perhaps the packaging script can also be made into a CMake target which just runs the packaging script directly, but it's just an enhancement.

The first job - the dependency build - has in part been tested.
However after a bit of playing around it has finally fallen short in the zlib build with space issues (see https://paste.kde.org/pmivxdpez)

Can you please take a look @alvinhochun?

rempt added a comment.Nov 8 2017, 10:17 AM

"C:\Program Files\mingw-w64\x86_64-7.1.0-posix-seh-rt_v5-rev2\mingw64\bin\windres.exe"

It really would be better to have mingw installed into c:\mingw-w64, if that's at all possible. This will keep being problematical.

Just don't install mingw-w64 under a path with spaces will be enough. It is a more-than-10-year-old bug with mingw toolchains so don't even think about trying.

You have got to be kidding. MingW is happy to allow itself to be installed into a path which it can't be used from? (and even defaults to said path!!)

*sigh*

I'll have a think about where to shift it. This isn't a promising start for MingW as a toolchain though I must say.

rempt added a comment.Nov 8 2017, 7:13 PM

Yeah... But unlike mscv it can build Vc... Just put it in c:\mingw-64 or something like that.

I seem to recall that at some point Krita was thinking about dropping Vc - is that something you're still thinking of doing?

rempt added a comment.Nov 8 2017, 8:37 PM

No, no! Not at all -- Vc is essential. What we were dropping was ms_vc_ :-)

I've now changed the install location of MinGW, it's extracting Qt now (it got past Qt before though).

PyQt has now failed in the install phase with 'access is denied'.
Any ideas? Don't suppose it would be trying to inject itself into the system Python installation would it?

Is there some place that I can check the log? It does sound a bit like it tries to install to /usr instead of the proper install directory.

Sorry, i've been running it manually on the server to test. Once we've got it up and running the logs will be accessible via Jenkins itself.

I've grabbed the whole of the PyQt5 log and put it at P133

Interesting, didn't notice this before. This means the following should be added:

diff --git a/3rdparty/ext_pyqt/CMakeLists.txt b/3rdparty/ext_pyqt/CMakeLists.txt
index bee5ad5bf9..e7ed7ae070 100644
--- a/3rdparty/ext_pyqt/CMakeLists.txt
+++ b/3rdparty/ext_pyqt/CMakeLists.txt
@@ -25,6 +25,7 @@ elseif(MINGW)
         --verbose
         --sipdir ${PREFIX_ext_pyqt}/share/sip
         --destdir ${PREFIX_ext_pyqt}/share/krita/pykrita
+        --stubdir ${PREFIX_ext_pyqt}/share/krita/pykrita
         --no-qml-plugin --no-python-dbus --no-qsci-api --no-tools
         --disable QtSql --disable QtTest --disable QtWinExtras
     )

I'm afraid that doesn't work:

configure.py: error: no such option: --stubdir

The proper fix would be

diff --git a/3rdparty/ext_pyqt/CMakeLists.txt b/3rdparty/ext_pyqt/CMakeLists.txt
index bee5ad5bf9..d19162942d 100644
--- a/3rdparty/ext_pyqt/CMakeLists.txt
+++ b/3rdparty/ext_pyqt/CMakeLists.txt
@@ -25,6 +25,7 @@ elseif(MINGW)
         --verbose
         --sipdir ${PREFIX_ext_pyqt}/share/sip
         --destdir ${PREFIX_ext_pyqt}/share/krita/pykrita
+        --stubsdir ${PREFIX_ext_pyqt}/share/krita/pykrita/PyQt5
         --no-qml-plugin --no-python-dbus --no-qsci-api --no-tools
         --disable QtSql --disable QtTest --disable QtWinExtras
     )

Thanks for fixing that. I can confirm the dependencies now build successfully.
I'll turn this into a Jenkins Pipeline and get it loaded into the Binary Factory, then proceed to start working on Krita.

rempt added a comment.Nov 20 2017, 1:14 PM

For packaging, we should build the installer with the executables after they're signed, because of: https://www.bitdefender.com/support/how-ransomware-protection-works-in-bitdefender-2017-1733.html

On the current master, I've added an arg --pre-zip-hook <script_path> to execute a script after doing all the necessary stuff but before making the zip package, which can be used to sign the binaries. I've also added a script packaging\windows\sign_package.cmd to do the signing. The script gets the signtool sign flags from the environment variable SIGNTOOL_SIGN_FLAGS. The script signs *.exe, *.dll and *.pyd (native Python module which is basically a dll) files which I think should be all the files that needs signing. I also put in a check for existing valid signature - if the file is already signed (e.g. by Microsoft or Python Software Foundation) it will skip signing for that file.

In short: Set the environment variable SIGNTOOL_SIGN_FLAGS (I tested with set "SIGNTOOL_SIGN_FLAGS=/f "C:\Users\Alvin\MySPC.pfx" /t http://timestamp.verisign.com/scripts/timstamp.dll"), then add --pre-zip-hook "path_to\sign_package.cmd" to the args for package-complete.cmd and it should produce a signed package.

The installer generation should run after the packaging script. I am still thinking how to deal with that... I think we should also need to be a way to enable generation of the installer only for a formal release and a way to specify the numeric version number for it.

I don't see the pre-zip-hook option in the current state of master. Can you confirm this has been pushed?

bcooksley closed this task as Resolved.Dec 17 2017, 9:49 AM
bcooksley claimed this task.

Following the provision of https://binary-factory.kde.org/job/Krita_Nightly_Build/ this is essentially resolved.

Once Alvin has come up with the script to produce the installers we can proceed to getting an installer generated as well along with a separate set of jobs which use source tarballs.