Skip to content

Commit

Permalink
Redesign Win32 build transition rules
Browse files Browse the repository at this point in the history
This reworks my previous commits [1][2][3], which aimed to make it
configurable on which executable should be built with what build
configurations (e.g. CRT link type, target CPU architecture).

One thing we need to further consider is the debug symbol name embedded
in each artifact executable [4].

To generate debug symbols one can use 'generate_pdb_file' feature. The
issue is that the pdb filename is not configurable in 'rules_cc'. For
instance, if the target name is 'mozc_tip32', then 'rules_cc' assumes
that the output pdb file is always 'mozc_tip32.pdb'. To make it
'mozc_tip32.dll.pdb', the target name must be 'mozc_tip32.dll.dll'.
This is why I had do rework Win32 build transition rules.

Implementation note:
 * The reason why 'mozc_win32_cc_prod_binary' needs to be introduced is
   because the final executable name needs to be available as an input
   of 'mozc_cc_binary'.
 * 'mozc_cc_win32_library' also needs to be reworked so that
   'generate_pdb_file' feature will not be applied to it.

There must be no observable change in GYP build and non-Windows bazel
builds.

Closes #1108.

 [1]: 5efa371
 [2]: ff64988
 [3]: ea55af0
 [4]: 0377ddd
  • Loading branch information
yukawa committed Dec 5, 2024
1 parent 8e7a307 commit 539dfc0
Show file tree
Hide file tree
Showing 12 changed files with 411 additions and 269 deletions.
1 change: 1 addition & 0 deletions src/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ bzl_library(
"//bazel:run_build_tool_bzl",
"//bazel:stubs.bzl",
"//devtools/build_cleaner/skylark:build_defs_lib",
"@bazel_skylib//rules:select_file",
"@build_bazel_rules_apple//apple:macos",
],
)
Expand Down
283 changes: 280 additions & 3 deletions src/build_defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ See also: https://bazel.build/rules/bzl-style#rules
"""

load("@bazel_skylib//rules:select_file.bzl", "select_file")
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application", "macos_bundle", "macos_unit_test")
load("@windows_sdk//:windows_sdk_rules.bzl", "windows_resource")
load(
"//:config.bzl",
"BAZEL_TOOLS_PREFIX",
"BRANDING",
"MACOS_BUNDLE_ID_PREFIX",
"MACOS_MIN_OS_VER",
Expand Down Expand Up @@ -203,6 +205,257 @@ register_extension_info(
label_regex_for_dep = "{extension_name}",
)

def _win_executable_transition_impl(
settings, # @unused
attr):
features = ["generate_pdb_file"]
if attr.static_crt:
features.append("static_link_msvcrt")
return {
"//command_line_option:features": features,
"//command_line_option:cpu": attr.cpu,
}

_win_executable_transition = transition(
implementation = _win_executable_transition_impl,
inputs = [],
outputs = [
"//command_line_option:features",
"//command_line_option:cpu",
],
)

def _mozc_win_build_rule_impl(ctx):
input_file = ctx.file.target
output = ctx.actions.declare_file(
ctx.label.name + ".symlink." + input_file.extension,
)
if input_file.path == output.path:
fail("input=%s and output=%s are the same." % (input_file.path, output.path))

# Create a symlink as we do not need to create an actual copy.
ctx.actions.symlink(
output = output,
target_file = input_file,
is_executable = True,
)
return [
DefaultInfo(
files = depset([output]),
executable = output,
),
OutputGroupInfo(
pdb_file = depset(ctx.files.pdb_file),
),
]

# The follwoing CPU values are mentioned in https://bazel.build/configure/windows#build_cpp
CPU = struct(
ARM64 = "arm64_windows", # aarch64 (64-bit) environment
X64 = "x64_windows", # x86-64 (64-bit) environment
X86 = "x64_x86_windows", # x86 (32-bit) environment
)

_mozc_win_build_rule = rule(
implementation = _mozc_win_build_rule_impl,
cfg = _win_executable_transition,
attrs = {
"_allowlist_function_transition": attr.label(
default = BAZEL_TOOLS_PREFIX + "//tools/allowlists/function_transition_allowlist",
),
"target": attr.label(
allow_single_file = [".dll", ".exe"],
doc = "the actual Bazel target to be built.",
mandatory = True,
),
"pdb_file": attr.label(
allow_files = True,
mandatory = True,
),
"static_crt": attr.bool(),
"cpu": attr.string(),
},
)

def mozc_win32_cc_prod_binary(
name,
executable_name_map = {},
srcs = [],
deps = [],
linkopts = [],
cpu = CPU.X64,
static_crt = False,
tags = MOZC_TAGS.WIN_ONLY,
win_def_file = None,
target_compatible_with = ["@platforms//os:windows"],
visibility = ["//visibility:public"],
**kwargs):
"""A rule to build production binaries for Windows.
This wraps mozc_cc_binary so that you can specify the target CPU
architecture and CRT linkage type in a declarative manner with also building
a debug symbol file (*.pdb).
Implicit output targets:
name.pdb: A debug symbol file.
Args:
name: name of the target.
executable_name_map: a map from the branding name to the executable name.
srcs: .cc files to build the executable.
deps: deps to build the executable.
linkopts: linker options to build the executable.
cpu: optional. The target CPU architecture.
static_crt: optional. True if the target should be built with static CRT.
tags: optional. Tags for both the library and unit test targets.
win_def_file: optional. win32 def file to define exported functions.
target_compatible_with: optional. Defines target platforms.
visibility: optional. The visibility of the target.
**kwargs: other arguments passed to mozc_cc_binary.
"""
mandatory_target_compatible_with = [
"@platforms//os:windows",
]
for item in mandatory_target_compatible_with:
if item not in target_compatible_with:
target_compatible_with.append(item)

mandatory_tags = MOZC_TAGS.WIN_ONLY
for item in mandatory_tags:
if item not in tags:
tags.append(item)

target_name = executable_name_map.get(BRANDING, None)
if target_name == None:
return

linkshared = False
intermediate_name = None
if target_name.endswith(".exe"):
# When the targete name is "foobar.exe", then "foobar.exe.dll" will be
# generated.
intermediate_name = target_name
elif target_name.endswith(".dll"):
# When the targete name is "foobar.dll", then "foobar.pdb" will be
# generated. To produce "foobar.dll.pdb", the target name needs to be
# something like "foobar.dll.dll".
intermediate_name = target_name + ".dll"
linkshared = True
else:
return

modified_linkopts = []
modified_linkopts.extend(linkopts)
modified_linkopts.extend([
"/DEBUG:FULL",
"/PDBALTPATH:%_PDB%",
])
mozc_cc_binary(
name = intermediate_name,
srcs = srcs,
deps = deps,
tags = tags,
linkshared = linkshared,
linkopts = modified_linkopts,
target_compatible_with = target_compatible_with,
visibility = ["//visibility:private"],
win_def_file = win_def_file,
**kwargs
)

native.filegroup(
name = intermediate_name + "_pdb_file",
srcs = [intermediate_name],
output_group = "pdb_file",
visibility = ["//visibility:private"],
)

_mozc_win_build_rule(
name = name,
target = intermediate_name,
pdb_file = intermediate_name + "_pdb_file",
cpu = cpu,
static_crt = static_crt,
target_compatible_with = target_compatible_with,
tags = tags,
visibility = visibility,
**kwargs
)

native.filegroup(
name = name + "_pdb_file",
srcs = [name],
output_group = "pdb_file",
visibility = ["//visibility:private"],
)

select_file(
name = name + ".pdb",
srcs = name + "_pdb_file",
subpath = target_name + ".pdb",
visibility = visibility,
)

register_extension_info(
extension = mozc_win32_cc_prod_binary,
label_regex_for_dep = "{extension_name}",
)

def _win_library_transition_impl(
settings, # @unused
attr): # @unused
return {
"//command_line_option:features": [],
}

def _mozc_win_library_transition_impl(ctx):
input_file = ctx.file.target
output = ctx.actions.declare_file(
ctx.label.name + "." + input_file.extension,
)
if input_file.path == output.path:
fail("input=%d and output=%d are the same." % (input_file.path, output.path))

# Create a symlink as we do not need to create an actual copy.
ctx.actions.symlink(
output = output,
target_file = input_file,
is_executable = True,
)
return [
DefaultInfo(
files = depset([output]),
),
OutputGroupInfo(
interface_library = depset(ctx.files.interface_library),
),
]

_mozc_cc_win32_library_rule = rule(
implementation = _mozc_win_library_transition_impl,
cfg = transition(
implementation = _win_library_transition_impl,
inputs = [],
outputs = [
"//command_line_option:features",
],
),
attrs = {
"_allowlist_function_transition": attr.label(
default = BAZEL_TOOLS_PREFIX + "//tools/allowlists/function_transition_allowlist",
),
"target": attr.label(
allow_single_file = [".dll"],
doc = "the actual Bazel target to be built.",
mandatory = True,
),
"interface_library": attr.label(
allow_files = [".lib"],
mandatory = True,
),
},
)

def mozc_cc_win32_library(
name,
srcs = [],
Expand All @@ -227,17 +480,25 @@ def mozc_cc_win32_library(
**kwargs: other args for cc_library.
"""

mandatory_target_compatible_with = [
"@platforms//os:windows",
]
for item in mandatory_target_compatible_with:
if item not in target_compatible_with:
target_compatible_with.append(item)

# A DLL name, which actually will not be used in production.
# e.g. "input_dll_fake.dll" vs "C:\Windows\System32\input.dll"
# The actual DLL name should be specified in the LIBRARY section of
# win_def_file.
# https://learn.microsoft.com/en-us/cpp/build/reference/library
cc_binary_target_name = name + "_fake.dll"
filegroup_target_name = name + "_lib"
cc_binary_target_intermediate_name = name + "_fake"
cc_binary_target_name = cc_binary_target_intermediate_name + ".dll"
filegroup_target_name = name + "_lib_group"
cc_import_taget_name = name + "_import"

mozc_cc_binary(
name = cc_binary_target_name,
name = cc_binary_target_intermediate_name,
srcs = srcs,
deps = deps,
win_def_file = win_def_file,
Expand All @@ -248,6 +509,22 @@ def mozc_cc_win32_library(
**kwargs
)

interface_library_name = filegroup_target_name + ".if.lib"
native.filegroup(
name = interface_library_name,
srcs = [":" + cc_binary_target_intermediate_name],
output_group = "interface_library",
tags = tags,
target_compatible_with = target_compatible_with,
visibility = ["//visibility:private"],
)

_mozc_cc_win32_library_rule(
name = cc_binary_target_name,
target = cc_binary_target_intermediate_name,
interface_library = interface_library_name,
)

native.filegroup(
name = filegroup_target_name,
srcs = [":" + cc_binary_target_name],
Expand Down
19 changes: 19 additions & 0 deletions src/gui/tool/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ load(
"MOZC_TAGS",
"mozc_cc_library",
"mozc_select",
"mozc_win32_cc_prod_binary",
"mozc_win32_resource_from_template",
)
load("//:config.bzl", "BRANDING")
Expand Down Expand Up @@ -132,6 +133,24 @@ mozc_cc_qt_binary(
),
)

mozc_win32_cc_prod_binary(
name = "mozc_tool_win",
cpu = "x64_windows",
executable_name_map = {
"Mozc": "mozc_tool.exe",
"GoogleJapaneseInput": "GoogleIMEJaTool.exe",
},
static_crt = False,
tags = MOZC_TAGS.WIN_ONLY,
target_compatible_with = ["@platforms//os:windows"],
visibility = ["//:__subpackages__"],
deps = [
":mozc_tool_main_lib",
":mozc_tool_win32_resource",
"@qt_win",
],
)

mozc_macos_qt_application(
name = "mozc_tool_macos",
bundle_name = "MozcTool",
Expand Down
9 changes: 8 additions & 1 deletion src/renderer/win32/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ load(
"mozc_cc_binary",
"mozc_cc_library",
"mozc_cc_test",
"mozc_win32_cc_prod_binary",
"mozc_win32_resource_from_template",
)

Expand All @@ -44,9 +45,15 @@ package(
features = ["gdi"],
)

mozc_cc_binary(
mozc_win32_cc_prod_binary(
name = "win32_renderer_main",
srcs = ["win32_renderer_main.cc"],
cpu = "x64_windows",
executable_name_map = {
"Mozc": "mozc_renderer.exe",
"GoogleJapaneseInput": "GoogleIMEJaRenderer.exe",
},
static_crt = False,
tags = MOZC_TAGS.WIN_ONLY,
target_compatible_with = ["@platforms//os:windows"],
visibility = ["//win32/installer:__subpackages__"], # Scheuklappen: keep
Expand Down
Loading

0 comments on commit 539dfc0

Please sign in to comment.