servo: Merge #11967 - Add Windows packaging to create an MSI installer (from metajack:windows-package); r=larsbergstrom
authorJack Moffitt <jack@metajack.im>
Mon, 25 Jul 2016 16:48:33 -0500
changeset 339364 2f05d0193c0df07b06ad75b714a587c80bb3d00d
parent 339363 26dcf6c717d198d35b9a1eb4204f4dd02d2fdbf1
child 339365 d2156242e9cf5b9916ac62c59fa2f32e62641c2b
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslarsbergstrom
servo: Merge #11967 - Add Windows packaging to create an MSI installer (from metajack:windows-package); r=larsbergstrom <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because they require manual testing <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 2746c476d3114d987b153d93bed2d302ba2e52a5
servo/components/servo/.cargo/config
servo/components/servo/fake-ld.cmd
servo/etc/ci/upload_nightly.sh
servo/python/servo/package_commands.py
servo/resources/Servo.ico
servo/support/windows/Servo.wxs.mako
servo/support/windows/fonts.conf
--- a/servo/components/servo/.cargo/config
+++ b/servo/components/servo/.cargo/config
@@ -4,8 +4,11 @@ ar = "arm-linux-androideabi-ar"
 
 [target.arm-unknown-linux-gnueabihf]
 linker = "arm-linux-gnueabihf-gcc"
 ar = "arm-linux-gnueabihf-ar"
 
 [target.aarch64-unknown-linux-gnu]
 linker = "aarch64-linux-gnu-gcc"
 ar = "aarch64-linux-gnu-ar"
+
+[target.'cfg(target_os=windows)']
+linker = "./fake-ld.cmd"
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/servo/components/servo/fake-ld.cmd
@@ -0,0 +1,2 @@
+@echo off
+gcc -mwindows %*
--- a/servo/etc/ci/upload_nightly.sh
+++ b/servo/etc/ci/upload_nightly.sh
@@ -36,18 +36,18 @@ main() {
         package=target/arm-linux-androideabi/release/*."${extension}"
     elif [[ "${platform}" == "linux" ]]; then
         extension=tar.gz
         package=target/*."${extension}"
     elif [[ "${platform}" == "mac" ]]; then
         extension=dmg
         package=target/*."${extension}"
     elif [[ "${platform}" == "windows" ]]; then
-        extension=tar.gz
-        package=target/*."${extension}"
+        extension=msi
+        package=target/msi/*.msi
     else
         usage >&2
         return 1
     fi
 
     # Lack of quotes on package allows glob expansion
     # Note that this is not robust in the case of embedded spaces
     # TODO(aneeshusa): make this glob robust using e.g. arrays or Python
--- a/servo/python/servo/package_commands.py
+++ b/servo/python/servo/package_commands.py
@@ -4,32 +4,37 @@
 # Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 # http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 # <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 # option. This file may not be copied, modified, or distributed
 # except according to those terms.
 
 from __future__ import print_function, unicode_literals
 
+import sys
+import os.path as path
+sys.path.append(path.join(path.dirname(sys.argv[0]), "components", "style", "properties", "Mako-0.9.1.zip"))
+
 import os
-import os.path as path
 import shutil
 import subprocess
 import tarfile
 
 from mach.registrar import Registrar
 from datetime import datetime
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
-from servo.command_base import CommandBase, cd, BuildNotFound, is_macosx
+from mako.template import Template
+
+from servo.command_base import CommandBase, cd, BuildNotFound, is_macosx, is_windows
 from servo.post_build_commands import find_dep_path_newest
 
 
 def delete(path):
     try:
         os.remove(path)         # Succeeds if path was a file
     except OSError:             # Or, if path was a directory...
         shutil.rmtree(path)     # Remove it and all its contents.
@@ -37,16 +42,21 @@ def delete(path):
 
 def otool(s):
     o = subprocess.Popen(['/usr/bin/otool', '-L', s], stdout=subprocess.PIPE)
     for l in o.stdout:
         if l[0] == '\t':
             yield l.split(' ', 1)[0][1:]
 
 
+def listfiles(directory):
+    return [f for f in os.listdir(directory)
+            if path.isfile(path.join(directory, f))]
+
+
 def install_name_tool(old, new, binary):
     try:
         subprocess.check_call(['install_name_tool', '-change', old, '@executable_path/' + new, binary])
     except subprocess.CalledProcessError as e:
         print("install_name_tool exited with return value %d" % e.returncode)
 
 
 @CommandProvider
@@ -71,32 +81,32 @@ class PackageCommands(CommandBase):
             if dev:
                 env["NDK_DEBUG"] = "1"
                 env["ANT_FLAVOR"] = "debug"
                 dev_flag = "-d"
             else:
                 env["ANT_FLAVOR"] = "release"
                 dev_flag = ""
 
-            target_dir = os.path.dirname(binary_path)
+            target_dir = path.dirname(binary_path)
             output_apk = "{}.apk".format(binary_path)
             try:
                 with cd(path.join("support", "android", "build-apk")):
                     subprocess.check_call(["cargo", "run", "--", dev_flag, "-o", output_apk, "-t", target_dir,
                                            "-r", self.get_top_dir()], env=env)
             except subprocess.CalledProcessError as e:
                 print("Packaging Android exited with return value %d" % e.returncode)
                 return e.returncode
         elif is_macosx():
             dir_to_build = '/'.join(binary_path.split('/')[:-1])
             dir_to_dmg = '/'.join(binary_path.split('/')[:-2]) + '/dmg'
             dir_to_app = dir_to_dmg + '/Servo.app'
             dir_to_resources = dir_to_app + '/Contents/Resources/'
             dir_to_root = '/'.join(binary_path.split('/')[:-3])
-            if os.path.exists(dir_to_dmg):
+            if path.exists(dir_to_dmg):
                 print("Cleaning up from previous packaging")
                 delete(dir_to_dmg)
             browserhtml_path = find_dep_path_newest('browserhtml', binary_path)
             if browserhtml_path is None:
                 print("Could not find browserhtml package; perhaps you haven't built Servo.")
                 return 1
 
             print("Copying files")
@@ -118,17 +128,17 @@ class PackageCommands(CommandBase):
                 checking = set(need_checked)
                 need_checked = set()
                 for f in checking:
                     # No need to check these for their dylibs
                     if '/System/Library' in f or '/usr/lib' in f:
                         continue
                     need_relinked = set(otool(f))
                     new_path = dir_to_app + '/Contents/MacOS/' + f.split('/')[-1]
-                    if not os.path.exists(new_path):
+                    if not path.exists(new_path):
                         shutil.copyfile(f, new_path)
                     for dylib in need_relinked:
                         if '/System/Library' in dylib or '/usr/lib' in dylib or 'servo' in dylib:
                             continue
                         install_name_tool(dylib, dylib.split('/')[-1], new_path)
                     need_checked.update(need_relinked)
                 checked.update(checking)
                 need_checked.difference_update(checked)
@@ -148,16 +158,55 @@ class PackageCommands(CommandBase):
             try:
                 subprocess.check_call(['hdiutil', 'create', '-volname', 'Servo', dmg_path, '-srcfolder', dir_to_dmg])
             except subprocess.CalledProcessError as e:
                 print("Packaging MacOS dmg exited with return value %d" % e.returncode)
                 return e.returncode
             print("Cleaning up")
             delete(dir_to_dmg)
             print("Packaged Servo into " + dmg_path)
+        elif is_windows():
+            dir_to_package = path.dirname(binary_path)
+            dir_to_root = self.get_top_dir()
+            dir_to_msi = path.join(dir_to_package, 'msi')
+            if path.exists(dir_to_msi):
+                print("Cleaning up from previous packaging")
+                delete(dir_to_msi)
+            os.makedirs(dir_to_msi)
+            top_path = dir_to_root
+            browserhtml_path = find_dep_path_newest('browserhtml', binary_path)
+            if browserhtml_path is None:
+                print("Could not find browserhtml package; perhaps you haven't built Servo.")
+                return 1
+            browserhtml_path = path.join(browserhtml_path, "out")
+            # generate Servo.wxs
+            template_path = path.join(dir_to_root, "support", "windows", "Servo.wxs.mako")
+            template = Template(open(template_path).read())
+            wxs_path = path.join(dir_to_msi, "Servo.wxs")
+            open(wxs_path, "w").write(template.render(
+                exe_path=dir_to_package,
+                top_path=top_path,
+                browserhtml_path=browserhtml_path))
+            # run candle and light
+            print("Creating MSI")
+            try:
+                with cd(dir_to_msi):
+                    subprocess.check_call(['candle', wxs_path])
+            except subprocess.CalledProcessError as e:
+                print("WiX candle exited with return value %d" % e.returncode)
+                return e.returncode
+            try:
+                wxsobj_path = "{}.wixobj".format(path.splitext(wxs_path)[0])
+                with cd(dir_to_msi):
+                    subprocess.check_call(['light', wxsobj_path])
+            except subprocess.CalledProcessError as e:
+                print("WiX light exited with return value %d" % e.returncode)
+                return e.returncode
+            msi_path = path.join(dir_to_msi, "Servo.msi")
+            print("Packaged Servo into {}".format(msi_path))
         else:
             dir_to_package = '/'.join(binary_path.split('/')[:-1])
             dir_to_root = '/'.join(binary_path.split('/')[:-3])
             shutil.copytree(dir_to_root + '/resources', dir_to_package + '/resources')
             browserhtml_path = find_dep_path_newest('browserhtml', binary_path)
             if browserhtml_path is None:
                 print("Could not find browserhtml package; perhaps you haven't built Servo.")
                 return 1
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..71d38a0c1478abf14b0de4cdaaeb198566b02a7c
GIT binary patch
literal 32988
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
new file mode 100644
--- /dev/null
+++ b/servo/support/windows/Servo.wxs.mako
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+  <Product Name="Servo Tech Demo"
+           Manufacturer="Mozilla Research"
+           Id="5807391a-3a17-476b-a5d2-5f1912569762"
+           UpgradeCode="060cd15d-eab1-4614-b438-3988e3efdcf1"
+           Language="1033"
+           Codepage="1252"
+           Version="1.0.0">
+    <Package Id="*"
+             Keywords="Installer"
+             Description="Servo Tech Demo Installer"
+             Manufacturer="Mozilla Research"
+             InstallerVersion="200"
+             Platform="x64"
+             Languages="1033"
+             SummaryCodepage="1252"
+             Compressed="yes"/>
+    <Media Id="1"
+           Cabinet="Servo.cab"
+           EmbedCab="yes"/>
+    <Directory Id="TARGETDIR" Name="SourceDir">
+      <Directory Id="ProgramFiles64Folder" Name="PFiles">
+        <Directory Id="MozResearch" Name="Mozilla Research">
+          <Directory Id="INSTALLDIR" Name="Servo Tech Demo">
+            <Component Id="Servo"
+                       Guid="95bcea71-78bb-4ec8-9766-44bc01443840"
+                       Win64="yes">
+              <File Id="ServoEXE"
+                    Name="servo.exe"
+                    DiskId="1"
+                    Source="${windowize(exe_path)}\servo.exe"
+                    KeyPath="yes">
+                <Shortcut Id="StartMenuServoTechDemo"
+                          Directory="ProgramMenuDir"
+                          Name="Servo Tech Demo"
+                          WorkingDirectory="INSTALLDIR"
+                          Icon="Servo.ico"
+                          IconIndex="0"
+                          Arguments="-w --pref dom.mozbrowser.enabled --pref shell.builtin-key-shortcuts.enabled=false browserhtml\index.html"
+                          Advertise="yes"/>
+              </File>
+              <File Id="ServoManifest"
+                    Name="servo.exe.manifest"
+                    Source="${windowize(exe_path)}\servo.exe.manifest"
+                    DiskId="1"/>
+
+              <File Id="StdcxxDLL"
+                    Name="libstdc++-6.dll"
+                    Source="C:\msys64\mingw64\bin\libstdc++-6.dll"
+                    DiskId="1"/>
+              <File Id="WinpthreadDll"
+                    Name="libwinpthread-1.dll"
+                    Source="C:\msys64\mingw64\bin\libwinpthread-1.dll"
+                    DiskId="1"/>
+              <File Id="Bzip2Dll"
+                    Name="libbz2-1.dll"
+                    Source="C:\msys64\mingw64\bin\libbz2-1.dll"
+                    DiskId="1"/>
+              <File Id="GccsehDll"
+                    Name="libgcc_s_seh-1.dll"
+                    Source="C:\msys64\mingw64\bin\libgcc_s_seh-1.dll"
+                    DiskId="1"/>
+              <File Id="ExpatDll"
+                    Name="libexpat-1.dll"
+                    Source="C:\msys64\mingw64\bin\libexpat-1.dll"
+                    DiskId="1"/>
+              <File Id="ZlibDll"
+                    Name="zlib1.dll"
+                    Source="C:\msys64\mingw64\bin\zlib1.dll"
+                    DiskId="1"/>
+              <File Id="PngDll"
+                    Name="libpng16-16.dll"
+                    Source="C:\msys64\mingw64\bin\libpng16-16.dll"
+                    DiskId="1"/>
+              <File Id="IconvDll"
+                    Name="libiconv-2.dll"
+                    Source="C:\msys64\mingw64\bin\libiconv-2.dll"
+                    DiskId="1"/>
+              <File Id="GlibDll"
+                    Name="libglib-2.0-0.dll"
+                    Source="C:\msys64\mingw64\bin\libglib-2.0-0.dll"
+                    DiskId="1"/>
+              <File Id="GraphiteDll"
+                    Name="libgraphite2.dll"
+                    Source="C:\msys64\mingw64\bin\libgraphite2.dll"
+                    DiskId="1"/>
+              <File Id="IntlDll"
+                    Name="libintl-8.dll"
+                    Source="C:\msys64\mingw64\bin\libintl-8.dll"
+                    DiskId="1"/>
+              <File Id="PcreDll"
+                    Name="libpcre-1.dll"
+                    Source="C:\msys64\mingw64\bin\libpcre-1.dll"
+                    DiskId="1"/>
+              <File Id="Eay32Dll"
+                    Name="libeay32.dll"
+                    Source="C:\msys64\mingw64\bin\libeay32.dll"
+                    DiskId="1"/>
+              <File Id="Ssleay32Dll"
+                    Name="ssleay32.dll"
+                    Source="C:\msys64\mingw64\bin\ssleay32.dll"
+                    DiskId="1"/>
+              <File Id="HarfbuzzDll"
+                    Name="libharfbuzz-0.dll"
+                    Source="C:\msys64\mingw64\bin\libharfbuzz-0.dll"
+                    DiskId="1"/>
+              <File Id="FreetypeDll"
+                    Name="libfreetype-6.dll"
+                    Source="C:\msys64\mingw64\bin\libfreetype-6.dll"
+                    DiskId="1"/>
+              <File Id="FontconfigDll"
+                    Name="libfontconfig-1.dll"
+                    Source="C:\msys64\mingw64\bin\libfontconfig-1.dll"
+                    DiskId="1"/>
+            </Component>
+
+            <Directory Id="EtcDir" Name="etc">
+              <Directory Id="FontsDir" Name="fonts">
+                <Component Id="FontsDir"
+                           Guid="8d37ee61-9237-438d-b976-f163bd6b0578"
+                           Win64="yes">
+                  <File Id="ServoFontsConfig"
+                        KeyPath="yes"
+                        Name="fonts.conf"
+                        Source="${windowize(top_path)}\support\windows\fonts.conf"
+                        DiskId="1"/>
+                </Component>
+              </Directory>
+            </Directory>
+
+            ${include_directory(path.join(top_path, "resources"), "resources")}
+            ${include_directory(browserhtml_path, "browserhtml")}
+          </Directory>
+        </Directory>
+      </Directory>
+
+      <Directory Id="ProgramMenuFolder" Name="Programs">
+        <Directory Id="ProgramMenuDir" Name="Servo Tech Demo">
+          <Component Id="ProgramMenuDir" Guid="e04737ce-16eb-4977-9b4c-ed2db8a5a77d">
+            <RemoveFolder Id="ProgramMenuDir" On="uninstall"/>
+            <RegistryValue Root="HKCU"
+                           Key="Software\Mozilla Research\Servo Tech Demo"
+                           Type="string"
+                           Value=""
+                           KeyPath="yes"/>
+          </Component>
+        </Directory>
+      </Directory>
+    </Directory>
+
+    <Feature Id="Complete" Level="1">
+      <ComponentRef Id="Servo"/>
+      <ComponentRef Id="FontsDir"/>
+      % for c in components:
+      <ComponentRef Id="${c}"/>
+      % endfor
+      <ComponentRef Id="ProgramMenuDir"/>
+    </Feature>
+
+    <Icon Id="Servo.ico" SourceFile="${windowize(top_path)}\resources\Servo.ico"/>
+  </Product>
+</Wix>
+<%!
+import os
+import os.path as path
+import re
+import uuid
+
+def make_id(s):
+    return "Id{}".format(s.replace("-", "_").replace("/", "_"))
+
+def listfiles(directory):
+    return [f for f in os.listdir(directory)
+            if path.isfile(path.join(directory, f))]
+
+def listdirs(directory):
+    return [f for f in os.listdir(directory)
+            if path.isdir(path.join(directory, f))]
+
+def windowize(p):
+    if not p.startswith("/"):
+        return p
+    return re.sub("^/([^/])+", "\\1:", p)
+
+components = []
+%>
+<%def name="include_directory(d, n)">
+<Directory Id="${make_id(path.basename(d))}" Name="${n}">
+  <Component Id="${make_id(path.basename(d))}"
+             Guid="${uuid.uuid4()}"
+             Win64="yes">
+    <CreateFolder/>
+    <% components.append(make_id(path.basename(d))) %>
+    % for f in listfiles(d):
+    <File Id="${make_id(path.join(d, f))}"
+          Name="${f}"
+          Source="${windowize(path.join(d, f))}"
+          DiskId="1"/>
+    % endfor
+  </Component>
+
+  % for f in listdirs(d):
+  ${include_directory(path.join(d, f), f)}
+  % endfor
+</Directory>
+</%def>
new file mode 100644
--- /dev/null
+++ b/servo/support/windows/fonts.conf
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<fontconfig>
+	<dir>C:\Windows\Fonts</dir>
+<!--
+  Accept deprecated 'mono' alias, replacing it with 'monospace'
+-->
+	<match target="pattern">
+		<test qual="any" name="family">
+			<string>mono</string>
+		</test>
+		<edit name="family" mode="assign" binding="same">
+			<string>monospace</string>
+		</edit>
+	</match>
+
+<!--
+  Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
+-->
+	<match target="pattern">
+		<test qual="any" name="family">
+			<string>sans serif</string>
+		</test>
+		<edit name="family" mode="assign" binding="same">
+			<string>sans-serif</string>
+		</edit>
+	</match>
+
+<!--
+  Accept deprecated 'sans' alias, replacing it with 'sans-serif'
+-->
+	<match target="pattern">
+		<test qual="any" name="family">
+			<string>sans</string>
+		</test>
+		<edit name="family" mode="assign" binding="same">
+			<string>sans-serif</string>
+		</edit>
+	</match>
+
+<!-- Font cache directory list -->
+
+	<cachedir>~/.fontconfig</cachedir>
+</fontconfig>