PR #24963: Build tensorflow.dll on Windows properly

Please approve this CL. It will be submitted automatically, and its GitHub pull request will be marked as merged.

Imported from GitHub PR #24963

1. To build a DLL for TensorFlow C API, you can now build
   `//tensorflow:tensorflow.dll`

2. The import library for tensorflow.dll can be built by
   `//tensorflow:tensorflow_dll_import_lib`

3. You can also get a compressed package for both (tensorflow.dll, tensorflow.lib) and C API headers by building
   `//tensorflow/tools/lib_package:libtensorflow`

3. Besides symbols for the official C API, symbols for
   building custom ops are also exported.

4. Also updated the libtensorflow_cpu.sh and libtensorflow_gpu.sh

Fixes #24885

Copybara import of the project:

  - 8e83669a07be02964a9185ccd0064cc967ecba76 Build tensorflow.dll on Windows properly by Yun Peng <pcloudy@google.com>
  - 302f7f93882bb7110ab9b7f6dee39d4b38897c6a No need to specifiy dll name explicitly by Yun Peng <pcloudy@google.com>
  - 03bae06e1e2e20aea06057f8d47d6f0301b2ef48 Merge 302f7f93882bb7110ab9b7f6dee39d4b38897c6a into 2e846... by Yun Peng <pcloudy@google.com>

PiperOrigin-RevId: 235934534
This commit is contained in:
A. Unique TensorFlower 2019-02-27 09:34:41 -08:00 committed by TensorFlower Gardener
parent 8e23a58c63
commit 0c8deb2f91
8 changed files with 222 additions and 93 deletions

View File

@ -16,6 +16,8 @@ exports_files([
])
load("//tensorflow:tensorflow.bzl", "tf_cc_shared_object")
load("//tensorflow:tensorflow.bzl", "tf_custom_op_library_additional_deps_impl")
load("//tensorflow:tensorflow.bzl", "tf_native_cc_binary")
load(
"//tensorflow/core:platform/default/build_config.bzl",
"tf_additional_binary_deps",
@ -499,19 +501,26 @@ tf_cc_shared_object(
# symbols in object files.
tf_cc_shared_object(
name = "libtensorflow.so",
name = "tensorflow",
linkopts = select({
"//tensorflow:darwin": [
"-Wl,-exported_symbols_list,$(location //tensorflow/c:exported_symbols.lds)",
"-Wl,-install_name,@rpath/libtensorflow.so",
],
"//tensorflow:windows": [],
"//tensorflow:windows": [
],
"//conditions:default": [
"-z defs",
"-Wl,--version-script,$(location //tensorflow/c:version_script.lds)",
],
}),
per_os_targets = True,
visibility = ["//visibility:public"],
# add win_def_file for tensorflow
win_def_file = select({
# We need this DEF file to properly export symbols on Windows "//tensorflow:windows": ":tensorflow_filtered_def_file",
"//conditions:default": None,
}),
deps = [
"//tensorflow/c:c_api",
"//tensorflow/c:c_api_experimental",
@ -523,7 +532,7 @@ tf_cc_shared_object(
)
tf_cc_shared_object(
name = "libtensorflow_cc.so",
name = "tensorflow_cc",
linkopts = select({
"//tensorflow:darwin": [
"-Wl,-exported_symbols_list,$(location //tensorflow:tf_exported_symbols.lds)",
@ -534,7 +543,13 @@ tf_cc_shared_object(
"-Wl,--version-script,$(location //tensorflow:tf_version_script.lds)",
],
}),
per_os_targets = True,
visibility = ["//visibility:public"],
# add win_def_file for tensorflow_cc
win_def_file = select({
# We need this DEF file to properly export symbols on Windows "//tensorflow:windows": ":tensorflow_filtered_def_file",
"//conditions:default": None,
}),
deps = [
"//tensorflow:tf_exported_symbols.lds",
"//tensorflow:tf_version_script.lds",
@ -548,6 +563,92 @@ tf_cc_shared_object(
] + if_ngraph(["@ngraph_tf//:ngraph_tf"]),
)
# ** Targets for Windows build (start) **
# Build a shared library (DLL) by cc_binary from tf_custom_op_library_additional_deps_impl,
# it contains all object code from its dependencies.
# This target is only used for parsing the symbols to be exported in tensorflow.dll.
# Do NOT depend on it.
tf_native_cc_binary(
name = "tf_custom_op_library_additional_deps.dll",
linkshared = 1,
linkstatic = 1,
deps = tf_custom_op_library_additional_deps_impl(),
)
# Get a DEF file generated by parsing all object files
# of tf_custom_op_library_additional_deps.so
filegroup(
name = "tensorflow_def_file",
srcs = [":tf_custom_op_library_additional_deps.dll"],
output_group = "def_file",
)
# Filter the DEF file to reduce the number of symbols to 64K or less.
# Note that we also write the name of the pyd file into DEF file so that
# the dynamic libraries of custom ops can find it at runtime.
genrule(
name = "tensorflow_filtered_def_file",
srcs = [":tensorflow_def_file"],
outs = ["tensorflow_filtered_def_file.def"],
cmd = select({
"//tensorflow:windows": """
$(location @local_config_def_file_filter//:def_file_filter) \\
--input $(location :tensorflow_def_file) \\
--output $@
""",
"//conditions:default": "touch $@", # Just a placeholder for Unix platforms
}),
tools = ["@local_config_def_file_filter//:def_file_filter"],
visibility = ["//visibility:public"],
)
# The interface library (tensorflow.dll.if.lib) for linking tensorflow DLL library (tensorflow.dll) on Windows.
# To learn more about import library (called interface library in Bazel):
# https://docs.microsoft.com/en-us/cpp/build/linking-an-executable-to-a-dll?view=vs-2017#linking-implicitly
filegroup(
name = "get_tensorflow_dll_import_lib",
srcs = ["//tensorflow:tensorflow.dll"],
output_group = "interface_library",
visibility = ["//visibility:public"],
)
# Rename the import library for tensorflow.dll from tensorflow.dll.if.lib to tensorflow.lib
genrule(
name = "tensorflow_dll_import_lib",
srcs = [":get_tensorflow_dll_import_lib"],
outs = ["tensorflow.lib"],
cmd = select({
"//tensorflow:windows": "cp -f $< $@",
"//conditions:default": "touch $@", # Just a placeholder for Unix platforms
}),
visibility = ["//visibility:public"],
)
# The interface library (tensorflow_cc.dll.if.lib) for linking tensorflow DLL library (tensorflow_cc.dll) on Windows.
# To learn more about import library (called interface library in Bazel):
# https://docs.microsoft.com/en-us/cpp/build/linking-an-executable-to-a-dll?view=vs-2017#linking-implicitly
filegroup(
name = "get_tensorflow_cc_dll_import_lib",
srcs = ["//tensorflow:tensorflow_cc.dll"],
output_group = "interface_library",
visibility = ["//visibility:public"],
)
# Rename the import library for tensorflow.dll from tensorflow_cc.dll.if.lib to tensorflow.lib
genrule(
name = "tensorflow_cc_dll_import_lib",
srcs = [":get_tensorflow_cc_dll_import_lib"],
outs = ["tensorflow_cc.lib"],
cmd = select({
"//tensorflow:windows": "cp -f $< $@",
"//conditions:default": "touch $@", # Just a placeholder for Unix platforms
}),
visibility = ["//visibility:public"],
)
# ** Targets for Windows build (end) **
exports_files(
[
"tf_version_script.lds",

View File

@ -362,6 +362,7 @@ tf_cc_test(
filegroup(
name = "libtensorflow_jni",
srcs = select({
"//tensorflow:windows": [":tensorflow_jni.dll"],
"//tensorflow:darwin": [":libtensorflow_jni.dylib"],
"//conditions:default": [":libtensorflow_jni.so"],
}),
@ -373,7 +374,7 @@ LINKER_VERSION_SCRIPT = ":config/version_script.lds"
LINKER_EXPORTED_SYMBOLS = ":config/exported_symbols.lds"
tf_cc_binary(
name = "libtensorflow_jni.so",
name = "tensorflow_jni",
# Set linker options to strip out anything except the JNI
# symbols from the library. This reduces the size of the library
# considerably (~50% as of January 2017).
@ -391,6 +392,7 @@ tf_cc_binary(
}),
linkshared = 1,
linkstatic = 1,
per_os_targets = True,
deps = [
"//tensorflow/core/distributed_runtime/rpc:grpc_server_lib",
"//tensorflow/java/src/main/native",
@ -412,14 +414,3 @@ tf_cc_binary(
srcs = ["generate_pom.cc"],
deps = ["//tensorflow/c:c_api"],
)
# System.loadLibrary() on OS X looks for ".dylib" or ".jnilib"
# and no ".so". If and when https://github.com/bazelbuild/bazel/issues/914
# is resolved, perhaps this workaround rule can be removed.
genrule(
name = "darwin-compat",
srcs = [":libtensorflow_jni.so"],
outs = ["libtensorflow_jni.dylib"],
cmd = "cp $< $@",
output_to_bindir = 1,
)

View File

@ -4338,7 +4338,7 @@ tf_py_wrap_cc(
"util/transform_graph.i",
"util/util.i",
],
# add win_def_file
# add win_def_file for pywrap_tensorflow
win_def_file = select({
"//tensorflow:windows": ":pywrap_tensorflow_filtered_def_file",
"//conditions:default": None,
@ -4396,43 +4396,27 @@ tf_py_wrap_cc(
# ** Targets for Windows build (start) **
# We need the following targets to expose symbols from _pywrap_tensorflow.dll
# Build a cc_binary from tf_custom_op_library_additional_deps_impl,
# it contains all object code from its dependencies.
tf_native_cc_binary(
name = "tf_custom_op_library_additional_deps.so",
linkshared = 1,
linkstatic = 1,
deps = tf_custom_op_library_additional_deps_impl(),
)
# Get a DEF file generated by parsing all object files
# of tf_custom_op_library_additional_deps.so
filegroup(
name = "pywrap_tensorflow_def_file",
srcs = [":tf_custom_op_library_additional_deps.so"],
output_group = "def_file",
)
# Filter the DEF file to reduce the number of symbols to 64K or less.
# Note that we also write the name of the pyd file into DEF file so that
# the dynamic libraries of custom ops can find it at runtime.
genrule(
name = "pywrap_tensorflow_filtered_def_file",
srcs = [":pywrap_tensorflow_def_file"],
srcs = ["//tensorflow:tensorflow_def_file"],
outs = ["pywrap_tensorflow_filtered_def_file.def"],
cmd = select({
"//tensorflow:windows": """
$(location @local_config_def_file_filter//:def_file_filter) \\
--input $(location :pywrap_tensorflow_def_file) \\
--input $(location //tensorflow:tensorflow_def_file) \\
--output $@ \\
--target _pywrap_tensorflow_internal.pyd
""",
"//conditions:default": "touch $@", # Just a placeholder for Unix platforms
}),
tools = ["@local_config_def_file_filter//:def_file_filter"],
visibility = ["//visibility:public"],
)
# Get the import library of _pywrap_tensorflow_internal.dll
# Get the import library of _pywrap_tensorflow_internal.pyd
filegroup(
name = "get_pywrap_tensorflow_import_lib_file",
srcs = [":_pywrap_tensorflow_internal.so"],

View File

@ -404,6 +404,17 @@ def tf_binary_dynamic_kernel_deps(kernels):
otherwise = kernels,
)
# Shared libraries have different name pattern on different platforms,
# but cc_binary cannot output correct artifact name yet,
# so we generate multiple cc_binary targets with all name patterns when necessary.
# TODO(pcloudy): Remove this workaround when https://github.com/bazelbuild/bazel/issues/4570
# is done and cc_shared_library is available.
SHARED_LIBRARY_NAME_PATTERNS = [
"lib%s.so", # On Linux, shared libraries are usually named as libfoo.so
"lib%s.dylib", # On macos, shared libraries are usually named as libfoo.dylib
"%s.dll", # On Windows, shared libraries are usually named as foo.dll
]
def tf_cc_shared_object(
name,
srcs = [],
@ -412,24 +423,42 @@ def tf_cc_shared_object(
linkopts = [],
framework_so = tf_binary_additional_srcs(),
kernels = [],
per_os_targets = False, # Generate targets with SHARED_LIBRARY_NAME_PATTERNS
visibility = None,
**kwargs):
native.cc_binary(
name = name,
srcs = srcs + framework_so,
deps = deps,
linkshared = 1,
data = data,
linkopts = linkopts + _rpath_linkopts(name) + select({
clean_dep("//tensorflow:darwin"): [
"-Wl,-install_name,@rpath/" + name.split("/")[-1],
],
clean_dep("//tensorflow:windows"): [],
"//conditions:default": [
"-Wl,-soname," + name.split("/")[-1],
],
}),
**kwargs
)
if per_os_targets:
names = [pattern % name for pattern in SHARED_LIBRARY_NAME_PATTERNS]
else:
names = [name]
for name_os in names:
native.cc_binary(
name = name_os,
srcs = srcs + framework_so,
deps = deps,
linkshared = 1,
data = data,
linkopts = linkopts + _rpath_linkopts(name_os) + select({
clean_dep("//tensorflow:darwin"): [
"-Wl,-install_name,@rpath/" + name_os.split("/")[-1],
],
clean_dep("//tensorflow:windows"): [],
"//conditions:default": [
"-Wl,-soname," + name_os.split("/")[-1],
],
}),
visibility = visibility,
**kwargs
)
if name not in names:
native.filegroup(
name = name,
srcs = select({
"//tensorflow:windows": [":%s.dll" % name],
"//tensorflow:darwin": [":lib%s.dylib" % name],
"//conditions:default": [":lib%s.so" % name],
}),
visibility = visibility,
)
register_extension_info(
extension_name = "tf_cc_shared_object",
@ -448,25 +477,43 @@ def tf_cc_binary(
linkopts = [],
copts = tf_copts(),
kernels = [],
per_os_targets = False, # Generate targets with SHARED_LIBRARY_NAME_PATTERNS
visibility = None,
**kwargs):
if kernels:
added_data_deps = tf_binary_dynamic_kernel_dsos()
else:
added_data_deps = []
native.cc_binary(
name = name,
copts = copts,
srcs = srcs + tf_binary_additional_srcs(),
deps = deps + tf_binary_dynamic_kernel_deps(kernels) + if_mkl_ml(
[
clean_dep("//third_party/mkl:intel_binary_blob"),
],
),
data = depset(data + added_data_deps),
linkopts = linkopts + _rpath_linkopts(name),
**kwargs
)
if per_os_targets:
names = [pattern % name for pattern in SHARED_LIBRARY_NAME_PATTERNS]
else:
names = [name]
for name_os in names:
native.cc_binary(
name = name_os,
copts = copts,
srcs = srcs + tf_binary_additional_srcs(),
deps = deps + tf_binary_dynamic_kernel_deps(kernels) + if_mkl_ml(
[
clean_dep("//third_party/mkl:intel_binary_blob"),
],
),
data = depset(data + added_data_deps),
linkopts = linkopts + _rpath_linkopts(name_os),
visibility = visibility,
**kwargs
)
if name not in names:
native.filegroup(
name = name,
srcs = select({
"//tensorflow:windows": [":%s.dll" % name],
"//tensorflow:darwin": [":lib%s.dylib" % name],
"//conditions:default": [":lib%s.so" % name],
}),
visibility = visibility,
)
register_extension_info(
extension_name = "tf_cc_binary",

View File

@ -31,20 +31,16 @@ if [ ! -e "WORKSPACE" ]; then
exit 1
fi
export TF_BAZEL_TARGETS="//tensorflow:libtensorflow.so"
export TF_BAZEL_TARGETS="${TF_BAZEL_TARGETS} //tensorflow/tools/lib_package:clicenses_generate"
export TF_BAZEL_TARGETS="${TF_BAZEL_TARGETS} //tensorflow/java:libtensorflow_jni.so"
export TF_BAZEL_TARGETS="${TF_BAZEL_TARGETS} //tensorflow/tools/lib_package:jnilicenses_generate"
run_configure_for_cpu_build
# build_libtensorflow_tarball in ../builds/libtensorflow.sh
# cannot be used on Windows since it relies on pkg_tar rules.
# So we do something special here
bazel --output_user_root=${TMPDIR} build -c opt --copt=/arch:AVX \
tensorflow:libtensorflow.so \
bazel --output_user_root=${TMPDIR} build -c opt --copt=/arch:AVX --announce_rc \
tensorflow:tensorflow.dll \
tensorflow:tensorflow_dll_import_lib \
tensorflow/tools/lib_package:clicenses_generate \
tensorflow/java:libtensorflow_jni.so \
tensorflow/java:tensorflow_jni.dll \
tensorflow/tools/lib_package:jnilicenses_generate
DIR=lib_package
@ -52,7 +48,7 @@ rm -rf ${DIR}
mkdir -p ${DIR}
# Zip up the .dll and the LICENSE for the JNI library.
cp bazel-bin/tensorflow/java/libtensorflow_jni.so ${DIR}/tensorflow_jni.dll
cp bazel-bin/tensorflow/java/tensorflow_jni.dll ${DIR}/tensorflow_jni.dll
zip -j ${DIR}/libtensorflow_jni-cpu-windows-$(uname -m).zip \
${DIR}/tensorflow_jni.dll \
bazel-genfiles/tensorflow/tools/lib_package/include/tensorflow/jni/LICENSE
@ -62,13 +58,15 @@ rm -f ${DIR}/tensorflow_jni.dll
mkdir -p ${DIR}/include/tensorflow/c
mkdir -p ${DIR}/include/tensorflow/c/eager
mkdir -p ${DIR}/lib
cp bazel-bin/tensorflow/libtensorflow.so ${DIR}/lib/tensorflow.dll
cp bazel-bin/tensorflow/tensorflow.dll ${DIR}/lib/tensorflow.dll
cp bazel-bin/tensorflow/tensorflow.lib ${DIR}/lib/tensorflow.lib
cp tensorflow/c/c_api.h ${DIR}/include/tensorflow/c
cp tensorflow/c/eager/c_api.h ${DIR}/include/tensorflow/c/eager
cp bazel-genfiles/tensorflow/tools/lib_package/include/tensorflow/c/LICENSE ${DIR}/include/tensorflow/c
cd ${DIR}
zip libtensorflow-cpu-windows-$(uname -m).zip \
lib/tensorflow.dll \
lib/tensorflow.lib \
include/tensorflow/c/eager/c_api.h \
include/tensorflow/c/c_api.h \
include/tensorflow/c/LICENSE

View File

@ -31,20 +31,16 @@ if [ ! -e "WORKSPACE" ]; then
exit 1
fi
export TF_BAZEL_TARGETS="//tensorflow:libtensorflow.so"
export TF_BAZEL_TARGETS="${TF_BAZEL_TARGETS} //tensorflow/tools/lib_package:clicenses_generate"
export TF_BAZEL_TARGETS="${TF_BAZEL_TARGETS} //tensorflow/java:libtensorflow_jni.so"
export TF_BAZEL_TARGETS="${TF_BAZEL_TARGETS} //tensorflow/tools/lib_package:jnilicenses_generate"
run_configure_for_gpu_build
# build_libtensorflow_tarball in ../builds/libtensorflow.sh
# cannot be used on Windows since it relies on pkg_tar rules.
# So we do something special here
bazel --output_user_root=${TMPDIR} build -c opt --copt=/arch:AVX --announce_rc \
tensorflow:libtensorflow.so \
tensorflow:tensorflow.dll \
tensorflow:tensorflow_dll_import_lib \
tensorflow/tools/lib_package:clicenses_generate \
tensorflow/java:libtensorflow_jni.so \
tensorflow/java:tensorflow_jni.dll \
tensorflow/tools/lib_package:jnilicenses_generate
DIR=lib_package
@ -52,7 +48,7 @@ rm -rf ${DIR}
mkdir -p ${DIR}
# Zip up the .dll and the LICENSE for the JNI library.
cp bazel-bin/tensorflow/java/libtensorflow_jni.so ${DIR}/tensorflow_jni.dll
cp bazel-bin/tensorflow/java/tensorflow_jni.dll ${DIR}/tensorflow_jni.dll
zip -j ${DIR}/libtensorflow_jni-gpu-windows-$(uname -m).zip \
${DIR}/tensorflow_jni.dll \
bazel-genfiles/tensorflow/tools/lib_package/include/tensorflow/jni/LICENSE
@ -62,13 +58,15 @@ rm -f ${DIR}/tensorflow_jni.dll
mkdir -p ${DIR}/include/tensorflow/c
mkdir -p ${DIR}/include/tensorflow/c/eager
mkdir -p ${DIR}/lib
cp bazel-bin/tensorflow/libtensorflow.so ${DIR}/lib/tensorflow.dll
cp bazel-bin/tensorflow/tensorflow.dll ${DIR}/lib/tensorflow.dll
cp bazel-bin/tensorflow/tensorflow.lib ${DIR}/lib/tensorflow.lib
cp tensorflow/c/c_api.h ${DIR}/include/tensorflow/c
cp tensorflow/c/eager/c_api.h ${DIR}/include/tensorflow/c/eager
cp bazel-genfiles/tensorflow/tools/lib_package/include/tensorflow/c/LICENSE ${DIR}/include/tensorflow/c
cd ${DIR}
zip -j libtensorflow-gpu-windows-$(uname -m).zip \
zip libtensorflow-gpu-windows-$(uname -m).zip \
lib/tensorflow.dll \
lib/tensorflow.lib \
include/tensorflow/c/eager/c_api.h \
include/tensorflow/c/c_api.h \
include/tensorflow/c/LICENSE

View File

@ -89,7 +89,7 @@ def get_args():
help="paths to input def file",
required=True)
parser.add_argument("--output", help="output deffile", required=True)
parser.add_argument("--target", help="name of the target", required=True)
parser.add_argument("--target", help="name of the target")
args = parser.parse_args()
return args
@ -119,7 +119,8 @@ def main():
taken = set()
# Header for the def file.
def_fp.write("LIBRARY " + args.target + "\n")
if args.target:
def_fp.write("LIBRARY " + args.target + "\n")
def_fp.write("EXPORTS\n")
def_fp.write("\t ??1OpDef@tensorflow@@UEAA@XZ\n")

View File

@ -7,6 +7,7 @@ load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
load("@local_config_syslibs//:build_defs.bzl", "if_not_system_lib")
load("//tensorflow:tensorflow.bzl", "tf_binary_additional_srcs")
load("//tensorflow:tensorflow.bzl", "if_cuda")
load("//tensorflow:tensorflow.bzl", "if_not_windows")
load("//third_party/mkl:build_defs.bzl", "if_mkl")
genrule(
@ -87,7 +88,13 @@ pkg_tar(
pkg_tar(
name = "clib",
files = ["//tensorflow:libtensorflow.so"],
files = select({
"//tensorflow:windows": [
"//tensorflow:tensorflow.dll",
"//tensorflow:tensorflow_dll_import_lib",
],
"//conditions:default": ["//tensorflow:libtensorflow.so"],
}),
package_dir = "lib",
# Mark as "manual" till
# https://github.com/bazelbuild/bazel/issues/2352
@ -133,7 +140,6 @@ genrule(
"@hwloc//:COPYING",
"@icu//:icu4c/LICENSE",
"@jpeg//:LICENSE.md",
"@llvm//:LICENSE.TXT",
"@lmdb//:LICENSE",
"@local_config_sycl//sycl:LICENSE.text",
"@nasm//:LICENSE",
@ -142,7 +148,9 @@ genrule(
"@protobuf_archive//:LICENSE",
"@snappy//:COPYING",
"@zlib_archive//:zlib.h",
] + select({
] + if_not_windows([
"@llvm//:LICENSE.TXT",
]) + select({
"//tensorflow:android": [],
"//tensorflow:ios": [],
"//tensorflow:linux_s390x": [],
@ -203,7 +211,6 @@ genrule(
"@hwloc//:COPYING",
"@icu//:icu4j/main/shared/licenses/LICENSE",
"@jpeg//:LICENSE.md",
"@llvm//:LICENSE.TXT",
"@lmdb//:LICENSE",
"@local_config_sycl//sycl:LICENSE.text",
"@nasm//:LICENSE",
@ -214,7 +221,9 @@ genrule(
"@zlib_archive//:zlib.h",
"@grpc//:LICENSE",
"@grpc//third_party/address_sorting:LICENSE",
] + select({
] + if_not_windows([
"@llvm//:LICENSE.TXT",
]) + select({
"//tensorflow:android": [],
"//tensorflow:ios": [],
"//tensorflow:linux_s390x": [],