Skip to content

Commit

Permalink
Reimplement whole archive on Windows
Browse files Browse the repository at this point in the history
Use wrapper script to get object files if /WHOLEARCHIVE is not supported.

fix #1978
work towards #1918

--
Change-Id: I675311478e65a1e1f3fa963187a5a8da531150d3
Reviewed-on: https://bazel-review.googlesource.com/#/c/6833
MOS_MIGRATED_REVID=137151817
  • Loading branch information
meteorcloudy authored and katre committed Oct 25, 2016
1 parent ca99bb7 commit 81aede1
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/test/shell/bazel/bazel_windows_example_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,48 @@ function test_cpp() {
assert_test_fails "//examples/cpp:hello-fail_test"
}

function test_cpp_alwayslink() {
mkdir -p cpp/main
cat >cpp/main/BUILD <<EOF
cc_library(
name = "lib",
srcs = ["lib.cc"],
alwayslink = 1,
)
cc_library(
name = "main",
srcs = ["main.cc"],
)
cc_binary(
name = "bin",
deps = [":main", ":lib"],
)
EOF

cat >cpp/main/lib.cc <<EOF
extern int global_variable;
int init() {
++global_variable;
return global_variable;
}
int x = init();
int y = init();
EOF

cat >cpp/main/main.cc <<EOF
#include<stdio.h>
int global_variable = 0;
int main(void) {
printf("global : %d\n", global_variable);
return 0;
}
EOF
assert_build //cpp/main:bin
./bazel-bin/cpp/main/bin >& $TEST_log \
|| fail "//cpp/main:bin execution failed"
expect_log "global : 2"
}

function test_java() {
local java_pkg=examples/java-native/src/main/java/com/example/myproject

Expand Down
170 changes: 170 additions & 0 deletions tools/cpp/CROSSTOOL.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,176 @@ toolchain {
}
}

action_config {
config_name: 'c++-link-executable'
action_name: 'c++-link-executable'
tool {
tool_path: 'wrapper/bin/msvc_link.bat'
}
implies: 'linkstamps'
implies: 'output_execpath_flags'
implies: 'input_param_flags'
implies: 'global_whole_archive'
}

action_config {
config_name: 'c++-link-dynamic-library'
action_name: 'c++-link-dynamic-library'
tool {
tool_path: 'wrapper/bin/msvc_link.bat'
}
implies: 'shared_flag'
implies: 'linkstamps'
implies: 'output_execpath_flags'
implies: 'input_param_flags'
implies: 'global_whole_archive'
implies: 'has_configured_linker_path'
}

action_config {
config_name: 'c++-link-static-library'
action_name: 'c++-link-static-library'
tool {
tool_path: 'wrapper/bin/msvc_link.bat'
}
implies: 'input_param_flags'
implies: 'global_whole_archive'
}

action_config {
config_name: 'c++-link-alwayslink-static-library'
action_name: 'c++-link-alwayslink-static-library'
tool {
tool_path: 'wrapper/bin/msvc_link.bat'
}
implies: 'input_param_flags'
implies: 'global_whole_archive'
}

# TODO(pcloudy): The following action_config is listed in MANDATORY_LINK_TARGET_TYPES.
# But do we really need them on Windows?
action_config {
config_name: 'c++-link-pic-static-library'
action_name: 'c++-link-pic-static-library'
tool {
tool_path: 'wrapper/bin/msvc_link.bat'
}
implies: 'input_param_flags'
implies: 'global_whole_archive'
}

action_config {
config_name: 'c++-link-alwayslink-pic-static-library'
action_name: 'c++-link-alwayslink-pic-static-library'
tool {
tool_path: 'wrapper/bin/msvc_link.bat'
}
implies: 'input_param_flags'
implies: 'global_whole_archive'
}

action_config {
config_name: 'c++-link-interface-dynamic-library'
action_name: 'c++-link-interface-dynamic-library'
tool {
tool_path: 'wrapper/bin/msvc_link.bat'
}
}

feature {
name: 'has_configured_linker_path'
}

feature {
name: 'shared_flag'
flag_set {
action: 'c++-link-dynamic-library'
flag_group {
flag: '/DLL'
}
}
}

feature {
name: 'linkstamps'
flag_set {
action: 'c++-link-executable'
action: 'c++-link-dynamic-library'
expand_if_all_available: 'linkstamp_paths'
flag_group {
flag: '%{linkstamp_paths}'
}
}
}

feature {
name: 'output_execpath_flags'
flag_set {
expand_if_all_available: 'output_execpath'
action: 'c++-link-executable'
action: 'c++-link-dynamic-library'
flag_group {
flag: '/OUT:%{output_execpath}'
}
}
}

feature {
name: 'input_param_flags'
flag_set {
expand_if_all_available: 'libopts'
action: 'c++-link-executable'
action: 'c++-link-dynamic-library'
action: 'c++-link-static-library'
action: 'c++-link-alwayslink-static-library'
action: 'c++-link-pic-static-library'
action: 'c++-link-alwayslink-pic-static-library'
flag_group {
flag: '%{libopts}'
}
}
flag_set {
expand_if_all_available: 'whole_archive_linker_params'
action: 'c++-link-executable'
action: 'c++-link-dynamic-library'
action: 'c++-link-static-library'
action: 'c++-link-alwayslink-static-library'
action: 'c++-link-pic-static-library'
action: 'c++-link-alwayslink-pic-static-library'
flag_group {
flag: '/WHOLEARCHIVE:%{whole_archive_linker_params}'
}
}
flag_set {
expand_if_all_available: 'linker_input_params'
action: 'c++-link-executable'
action: 'c++-link-dynamic-library'
action: 'c++-link-static-library'
action: 'c++-link-alwayslink-static-library'
action: 'c++-link-pic-static-library'
action: 'c++-link-alwayslink-pic-static-library'
flag_group {
flag: '%{linker_input_params}'
}
}
}

feature {
name: 'global_whole_archive'
flag_set {
expand_if_all_available: 'global_whole_archive'
action: 'c++-link-executable'
action: 'c++-link-dynamic-library'
action: 'c++-link-static-library'
action: 'c++-link-alwayslink-static-library'
action: 'c++-link-pic-static-library'
action: 'c++-link-alwayslink-pic-static-library'
flag_group {
flag: '/WHOLEARCHIVE'
}
}
}

compilation_mode_flags {
mode: DBG
compiler_flag: "/DDEBUG=1"
Expand Down
13 changes: 13 additions & 0 deletions tools/cpp/cc_configure.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ def _find_env_vars(repository_ctx, vs_path):
return env_map


def _is_support_whole_archive(repository_ctx, vs_dir):
"""Run MSVC linker alone to see if it supports /WHOLEARCHIVE."""
result = _execute(repository_ctx, [vs_dir + "/VC/BIN/amd64/link"])
return result.find("/WHOLEARCHIVE") != -1


def _tpl(repository_ctx, tpl, substitutions={}, out=None):
if not out:
out = tpl
Expand Down Expand Up @@ -476,12 +482,19 @@ def _impl(repository_ctx):
python_dir = python_binary[0:-10].replace("\\", "\\\\")
include_paths = env["INCLUDE"] + (python_dir + "include")
lib_paths = env["LIB"] + (python_dir + "libs")
lib_tool = vs_path + "/VC/bin/amd64/lib.exe"
if _is_support_whole_archive(repository_ctx, vs_path):
support_whole_archive = "True"
else:
support_whole_archive = "False"
tmp_dir = _get_env_var(repository_ctx, "TMP", "C:\\Windows\\Temp")
_tpl(repository_ctx, "wrapper/bin/pydir/msvc_tools.py", {
"%{tmp}": tmp_dir.replace("\\", "\\\\"),
"%{path}": env["PATH"],
"%{include}": include_paths,
"%{lib}": lib_paths,
"%{lib_tool}": lib_tool,
"%{support_whole_archive}": support_whole_archive
})

cxx_include_directories = []
Expand Down
3 changes: 3 additions & 0 deletions tools/cpp/wrapper/bin/pydir/msvc_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def Run(self, argv):
# Build argument list.
parser = msvc_tools.ArgParser(self, argv, LINKPATTERNS)

# Preprocessing arguments for linking whole archive libraries
parser.WholeArchivePreprocess()

# Find the output file name.
name = ''
for arg in parser.options:
Expand Down
31 changes: 31 additions & 0 deletions tools/cpp/wrapper/bin/pydir/msvc_tools.py.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ TMP_PATH = '%{tmp}'
PATH = "%{path}"
INCLUDE = "%{include}"
LIB = "%{lib}"
LIB_TOOL = "%{lib_tool}"

class Error(Exception):
"""Base class for all script-specific errors."""
Expand All @@ -52,8 +53,38 @@ class ArgParser(object):
self.deps_file = None
self.output_file = None
self.params_file = None
self.support_whole_archive = %{support_whole_archive}
self.need_global_whole_archive = None
self._ParseArgs(argv)

def ReplaceLibrary(self, arg):
"""Do the actual replacement if necessary."""
if arg == "/WHOLEARCHIVE":
return []
if arg.startswith("/OUT:") or os.path.splitext(arg)[1] not in ['.a', '.lo']:
return [arg]
if self.global_whole_archive or arg.startswith("/WHOLEARCHIVE:"):
if arg.startswith("/WHOLEARCHIVE:"):
arg = arg[len("/WHOLEARCHIVE:"):]
output = subprocess.check_output([LIB_TOOL, "/list", arg]).decode("utf-8")
object_files = []
for line in output.split("\n"):
line = line.strip()
if line.endswith(".o"):
object_files.append(line)
return object_files
return [arg]

def WholeArchivePreprocess(self):
"""Replace library file with object files if /WHOLEARCHIVE is not supported."""
if self.support_whole_archive:
return
options = []
self.global_whole_archive = "/WHOLEARCHIVE" in self.options
for arg in self.options:
options.extend(self.ReplaceLibrary(arg))
self.options = options

def _MatchOneArg(self, args):
"""Finds a pattern which matches the beginning elements of args.

Expand Down

0 comments on commit 81aede1

Please sign in to comment.