Skip to content

Commit

Permalink
Support building installer for Windows with Bazel
Browse files Browse the repository at this point in the history
As a milestone of our on-going effort to support Bazel to build Mozc
for Windows (#948), we can finally start building the installer of Mozc
for Windows with Bazel with this commit.

While there remain several things to be followed up (e.g. compiler
options and linker options for production builds), it should now just
work in an end-to-end manner.

PiperOrigin-RevId: 680400073
  • Loading branch information
yukawa authored and hiroyuki-komatsu committed Sep 30, 2024
1 parent 4f5ca38 commit ffc460e
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 2 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,14 @@ jobs:
env:
ANDROID_NDK_HOME: ""
run: |
bazel --bazelrc=windows.bazelrc build //server:mozc_server //gui/tool:mozc_tool --config oss_windows --verbose_failures --cpu=x64_windows
bazel --bazelrc=windows.bazelrc build --config oss_windows --config release_build package
- name: upload Mozc64_bazel.msi
uses: actions/upload-artifact@v4
with:
name: Mozc64_bazel.msi
path: src/bazel-bin/win32/installer/Mozc64.msi
if-no-files-found: warn

test:
# https://github.com/actions/runner-images/blob/main/images/windows/Windows2022-Readme.md
Expand Down
10 changes: 10 additions & 0 deletions docs/build_mozc_in_windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ Note that you can specify `--qtdir=` option instead of `--noqt` in GYP phase sin

---

## Build with Bazel (experimental)

```
bazel --bazelrc=windows.bazelrc build --config oss_windows --config release_build package
```

You have release build binaries in `bazel-bin\win32\installer\Mozc.msi`.

---

## Build with GitHub Actions

GitHub Actions are already set up in [windows.yaml](../.github/workflows/windows.yaml). With that, you can build and install Mozc with your own commit as follows.
Expand Down
1 change: 1 addition & 0 deletions src/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ filegroup(
default = [],
linux = ["//unix:package"],
macos = ["//mac:package"],
windows = ["//win32/installer"],
),
)

Expand Down
1 change: 1 addition & 0 deletions src/bazel/BUILD.qt_win.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ exports_files([
"bin/moc.exe",
"bin/uic.exe",
"bin/rcc.exe",
"bin/Qt6Core.dll",
])
5 changes: 5 additions & 0 deletions src/build_tools/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,8 @@ mozc_py_binary(
srcs = ["gen_win32_resource_header.py"],
deps = [":mozc_version_lib"],
)

mozc_py_library(
name = "vs_util",
srcs = ["vs_util.py"],
)
4 changes: 4 additions & 0 deletions src/data/images/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ exports_files([
"hiragana.svg",
"icon.svg",
])

exports_files([
"product_icon.ico",
])
77 changes: 76 additions & 1 deletion src/win32/installer/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@

# Build rules for the Windows installer.

load("//:build_defs.bzl", "MOZC_TAGS")
load("//:build_defs.bzl", "MOZC_TAGS", "mozc_py_binary")
load("//:config.bzl", "BRANDING")
load(":transition.bzl", "mozc_win_build_target")

package(default_visibility = ["//visibility:private"])
Expand Down Expand Up @@ -104,3 +105,77 @@ mozc_win_build_target(
target = "//win32/custom_action",
target_compatible_with = _TARGET_COMPATIBLE_WITH,
)

mozc_py_binary(
name = "build_installer",
srcs = ["build_installer.py"],
test_lib = False,
deps = [
"//build_tools:mozc_version_lib",
"//build_tools:vs_util",
],
)

exports_files([
"installer_64bit.wxs",
"installer_oss_64bit.wxs",
])

config_setting(
name = "debug_build",
values = {
"compilation_mode": "dbg",
},
visibility = ["//visibility:private"],
)

_WXS_FILE = ":installer_oss_64bit.wxs" if BRANDING == "Mozc" else ":installer_64bit.wxs"

_MSI_FILE = "Mozc.msi" if BRANDING == "Mozc" else "GoogleJapaneseInput.msi"

genrule(
name = "installer",
srcs = [
":mozc_tool",
":mozc_renderer",
":mozc_server",
":mozc_broker",
":mozc_cache_service",
":mozc_tip32",
":mozc_tip64",
":custom_action",
_WXS_FILE,
"//base:mozc_version_txt",
"//data/images/win:product_icon.ico",
"//data/installer:credits_en.html",
"@qt_win//:bin/Qt6Core.dll",
"@wix//:wix.exe",
],
outs = [_MSI_FILE],
cmd = " ".join([
"$(location :build_installer)",
"--output=$@",
"--version_file=$(location //base:mozc_version_txt)",
"--mozc_tool=$(location :mozc_tool)",
"--mozc_renderer=$(location :mozc_renderer)",
"--mozc_server=$(location :mozc_server)",
"--mozc_broker=$(location :mozc_broker)",
"--mozc_cache_service=$(location :mozc_cache_service)",
"--mozc_tip32=$(location :mozc_tip32)",
"--mozc_tip64=$(location :mozc_tip64)",
"--custom_action=$(location :custom_action)",
"--icon_path=$(location //data/images/win:product_icon.ico)",
"--credit_file=$(location //data/installer:credits_en.html)",
"--qt_core_dll=$(location @qt_win//:bin/Qt6Core.dll)",
"--wxs_path=$(location " + _WXS_FILE + ")",
"--wix_path=$(location @wix//:wix.exe)",
"--branding=" + BRANDING,
]) + select({
":debug_build": " --debug_build",
"//conditions:default": "",
}),
target_compatible_with = _TARGET_COMPATIBLE_WITH,
tools = [
":build_installer",
],
)
177 changes: 177 additions & 0 deletions src/win32/installer/build_installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2021, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Runs wix.exe to build the installer."""

import argparse
import os
import pathlib
import subprocess

from build_tools import mozc_version
from build_tools import vs_util


def exec_command(args: list[str], cwd: str) -> None:
"""Runs the given command then returns the output.
Args:
args: The command to be executed.
cwd: The current working directory.
Raises:
ChildProcessError: When the given command cannot be executed.
"""
process = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=False,
text=True,
encoding='utf-8',
cwd=cwd)
stdout, stderr = process.communicate()
exitcode = process.wait()

if exitcode != 0:
command = ' '.join(args)
msgs = ['Failed to execute command:', '', command, '', f'cwd={cwd}']
if stdout:
msgs += ['-----stdout-----', stdout]
if stderr:
msgs += ['-----stderr-----', stderr]
raise ChildProcessError('\n'.join(msgs))


def run_wix4(args) -> None:
"""Run 'dotnet tool run wix build ...'.
Args:
args: args
"""
vs_env_vars = vs_util.get_vs_env_vars('x64')
redist_root = pathlib.Path(vs_env_vars['VCTOOLSREDISTDIR']).resolve()
redist_x86 = redist_root.joinpath('x86').joinpath('Microsoft.VC143.CRT')
redist_x64 = redist_root.joinpath('x64').joinpath('Microsoft.VC143.CRT')
version_file = pathlib.Path(args.version_file).resolve()
version = mozc_version.MozcVersion(version_file)
credit_file = pathlib.Path(args.credit_file).resolve()
document_dir = credit_file.parent
qt_dir = pathlib.Path(args.qt_core_dll).parent.parent.resolve()
icon_path = pathlib.Path(args.icon_path).resolve()
mozc_tip32 = pathlib.Path(args.mozc_tip32).resolve()
mozc_tip64 = pathlib.Path(args.mozc_tip64).resolve()
mozc_broker = pathlib.Path(args.mozc_broker).resolve()
mozc_server = pathlib.Path(args.mozc_server).resolve()
mozc_cache_service = pathlib.Path(args.mozc_cache_service).resolve()
mozc_renderer = pathlib.Path(args.mozc_renderer).resolve()
mozc_tool = pathlib.Path(args.mozc_tool).resolve()
custom_action = pathlib.Path(args.custom_action).resolve()
wix_path = pathlib.Path(args.wix_path).resolve()

branding = args.branding
upgrade_code = ''
omaha_guid = ''
omaha_client_key = ''
omaha_clientstate_key = ''
if branding == 'GoogleJapaneseInput':
upgrade_code = 'C1A818AF-6EC9-49EF-ADCF-35A40475D156'
omaha_guid = 'DDCCD2A9-025E-4142-BCEB-F467B88CF830'
omaha_client_key = 'Software\\Google\\Update\\Clients\\' + omaha_guid
omaha_clientstate_key = (
'Software\\Google\\Update\\ClientState\\' + omaha_guid
)
elif branding == 'Mozc':
upgrade_code = 'DD94B570-B5E2-4100-9D42-61930C611D8A'

omaha_channel_type = 'dev' if version.IsDevChannel() else 'stable'
vs_configuration_name = 'Debug' if args.debug_build else 'Release'

commands = [
f'{wix_path}',
'build',
'-nologo',
'-arch', 'x64',
'-define', f'MozcVersion={version.GetVersionString()}',
'-define', f'UpgradeCode={upgrade_code}',
'-define', f'OmahaGuid={omaha_guid}',
'-define', f'OmahaClientKey={omaha_client_key}',
'-define', f'OmahaClientStateKey={omaha_clientstate_key}',
'-define', f'OmahaChannelType={omaha_channel_type}',
'-define', f'VSConfigurationName={vs_configuration_name}',
'-define', f'ReleaseRedistCrt32Dir={redist_x86}',
'-define', f'ReleaseRedistCrt64Dir={redist_x64}',
'-define', f'AddRemoveProgramIconPath={icon_path}',
'-define', f'MozcTIP32Path={mozc_tip32}',
'-define', f'MozcTIP64Path={mozc_tip64}',
'-define', f'MozcBroker64Path={mozc_broker}',
'-define', f'MozcServer64Path={mozc_server}',
'-define', f'MozcCacheService64Path={mozc_cache_service}',
'-define', f'MozcRenderer64Path={mozc_renderer}',
'-define', f'MozcToolPath={mozc_tool}',
'-define', f'CustomActions64Path={custom_action}',
'-define', f'DocumentsDir={document_dir}',
'-define', f'QtDir={qt_dir}',
'-define', 'QtVer=6',
'-out', args.output,
'-src', args.wxs_path,
]
exec_command(commands, cwd=os.getcwd())


def main():
parser = argparse.ArgumentParser()
parser.add_argument('--output', type=str)
parser.add_argument('--version_file', type=str)
parser.add_argument('--mozc_tool', type=str)
parser.add_argument('--mozc_renderer', type=str)
parser.add_argument('--mozc_server', type=str)
parser.add_argument('--mozc_broker', type=str)
parser.add_argument('--mozc_cache_service', type=str)
parser.add_argument('--mozc_tip32', type=str)
parser.add_argument('--mozc_tip64', type=str)
parser.add_argument('--custom_action', type=str)
parser.add_argument('--icon_path', type=str)
parser.add_argument('--credit_file', type=str)
parser.add_argument('--qt_core_dll', type=str)
parser.add_argument('--wxs_path', type=str)
parser.add_argument('--wix_path', type=str)
parser.add_argument('--branding', type=str)
parser.add_argument(
'--debug_build', dest='debug_build', default=False, action='store_true'
)

args = parser.parse_args()

run_wix4(args)


if __name__ == '__main__':
main()

0 comments on commit ffc460e

Please sign in to comment.