Skip to content

Commit

Permalink
DALI TF stop requiring DALI to be installed before build_ext step (NV…
Browse files Browse the repository at this point in the history
…IDIA#2204)

Signed-off-by: Joaquin Anton <[email protected]>
  • Loading branch information
jantonguirao authored Aug 12, 2020
1 parent a3aad8c commit b625495
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 43 deletions.
2 changes: 2 additions & 0 deletions dali_tf_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ configure_file("${PROJECT_SOURCE_DIR}/setup.py.in" "${PROJECT_BINARY_DIR}/setup.
configure_file("${PROJECT_SOURCE_DIR}/nvidia/dali_tf_plugin/__init__.py.in" "${PROJECT_BINARY_DIR}/nvidia/dali_tf_plugin/__init__.py")
install(FILES "${PROJECT_SOURCE_DIR}/nvidia/dali_tf_plugin/dali_tf_plugin.py" DESTINATION "${PROJECT_BINARY_DIR}/nvidia/dali_tf_plugin/")

install(DIRECTORY "${DALI_ROOT}/include" DESTINATION "${PROJECT_BINARY_DIR}")
install(FILES "${DALI_ROOT}/tools/stubgen.py" DESTINATION "${PROJECT_BINARY_DIR}")
install(FILES "daliop.cc" DESTINATION "${PROJECT_BINARY_DIR}")
install(FILES "dali_dataset_op.cc" DESTINATION "${PROJECT_BINARY_DIR}")
install(FILES "dali_shape_helper.h" DESTINATION "${PROJECT_BINARY_DIR}")
Expand Down
4 changes: 2 additions & 2 deletions dali_tf_plugin/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
include Acknowledgements.txt LICENSE COPYRIGHT
include *.cc
include *.h
recursive-include . *.cc
recursive-include . *.h
recursive-include . *.py
recursive-include prebuilt *.so
17 changes: 13 additions & 4 deletions dali_tf_plugin/build_dali_tf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@ LIB_NAME=${1:-"libdali_tf_current.so"}
SRCS="daliop.cc dali_dataset_op.cc"
INCL_DIRS="-I/usr/local/cuda/include/"

DALI_CFLAGS=( $($PYTHON ./dali_compile_flags.py --include_flags) )
DALI_LFLAGS=( $($PYTHON ./dali_compile_flags.py --lflags) )
DALI_STUB_DIR=`mktemp -d -t "dali_stub_XXXXXX"`
DALI_STUB_SRC="${DALI_STUB_DIR}/dali_stub.cc"
DALI_STUB_LIB="${DALI_STUB_DIR}/libdali.so"
$PYTHON ../tools/stubgen.py ../include/dali/c_api.h --output "${DALI_STUB_SRC}"

DALI_CFLAGS="-I../include"
DALI_LFLAGS="-L${DALI_STUB_DIR} -ldali"

$COMPILER -std=c++11 -DNDEBUG -O2 -shared -fPIC ${DALI_STUB_SRC} -o ${DALI_STUB_LIB} ${INCL_DIRS} ${DALI_CFLAGS}

TF_CFLAGS=( $($PYTHON -c 'import tensorflow as tf; print(" ".join(tf.sysconfig.get_compile_flags()))') )
TF_LFLAGS=( $($PYTHON -c 'import tensorflow as tf; print(" ".join(tf.sysconfig.get_link_flags()))') )


# Note: DNDEBUG flag is needed due to issue with TensorFlow custom ops:
# https://github.com/tensorflow/tensorflow/issues/17316
# Do not remove it.
$COMPILER -std=c++11 -DNDEBUG -O2 -shared -fPIC ${SRCS} -o ${LIB_NAME} ${INCL_DIRS} ${TF_CFLAGS[@]} ${TF_LFLAGS[@]} ${DALI_CFLAGS[@]} ${DALI_LFLAGS[@]}
$COMPILER -std=c++11 -DNDEBUG -O2 -shared -fPIC ${SRCS} -o ${LIB_NAME} ${INCL_DIRS} ${DALI_CFLAGS} ${DALI_LFLAGS} ${TF_CFLAGS[@]} ${TF_LFLAGS[@]}

# Cleaning up stub dir
rm -rf "${DALI_STUB_DIR}"
63 changes: 38 additions & 25 deletions dali_tf_plugin/dali_tf_plugin_install_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import os
from distutils.version import StrictVersion
from pathlib import Path
import tempfile
from stubgen import stubgen

class InstallerHelper:
def __init__(self, plugin_dest_dir = None):
Expand Down Expand Up @@ -118,12 +120,6 @@ def install(self):
error_msg += '\n' + self.debug_str()
raise ImportError(error_msg)

if self.dali_lib_path == "":
error_msg = "Installation error:"
error_msg += "\n DALI installation not found. Install `nvidia-dali` and try again"
error_msg += '\n' + self.debug_str()
raise ImportError(error_msg)

compiler = self.cpp_compiler

# From tensorflow team (https://github.com/tensorflow/tensorflow/issues/29643):
Expand Down Expand Up @@ -156,27 +152,44 @@ def install(self):
raise ImportError(error_msg)

print("Proceed with build...")
dali_cflags, dali_lflags = get_dali_build_flags()
tf_cflags, tf_lflags = get_tf_build_flags()
cuda_cflags, cuda_lflags = get_cuda_build_flags()

filenames = ['daliop.cc', 'dali_dataset_op.cc']
plugin_src = ''
for filename in filenames:
plugin_src = plugin_src + ' ' + os.path.join(self.src_path, filename)

lib_path = os.path.join(self.plugin_dest_dir, 'libdali_tf_current.so')

# Note: DNDEBUG flag is needed due to issue with TensorFlow custom ops:
# https://github.com/tensorflow/tensorflow/issues/17316
# Do not remove it.
cmd = compiler + ' -Wl,-R,\'$ORIGIN/..\' -std=c++11 -DNDEBUG -shared ' \
+ plugin_src + ' -o ' + lib_path + ' -fPIC ' + dali_cflags + ' ' \
+ tf_cflags + ' ' + cuda_cflags + ' ' + dali_lflags + ' ' + tf_lflags + ' ' \
+ cuda_lflags + ' -O2'
print("Build command:\n\n " + cmd + '\n\n')
subprocess.check_call(cmd, cwd=self.src_path, shell=True)

with tempfile.TemporaryDirectory(prefix="dali_stub_") as tmpdir:
# Building a DALI stub library. During runtime, the real libdali.so will be already loaded at the moment when the DALI TF plugin is loaded
# This is done to avoid depending on DALI being installed during DALI TF sdist installation
dali_stub_src = os.path.join(tmpdir, 'dali_stub.cc')
dali_stub_lib = os.path.join(tmpdir, 'libdali.so')
dali_c_api_hdr = os.path.join(self.src_path, 'include', 'dali', 'c_api.h')
with open(dali_stub_src, 'w+') as f:
stubgen(header_filepath=dali_c_api_hdr, out_file=f)

dali_lflags = '-L' + tmpdir + ' -ldali'
dali_cflags = '-I' + os.path.join(self.src_path, 'include')

cmd = compiler + ' -Wl,-R,\'$ORIGIN/..\' -std=c++11 -DNDEBUG -shared ' \
+ dali_stub_src + ' -o ' + dali_stub_lib + ' -fPIC ' + dali_cflags + ' ' \
+ cuda_cflags + ' ' + cuda_lflags + ' -O2'
print('Building DALI stub lib:\n\n ' + cmd + '\n\n')
subprocess.check_call(cmd, cwd=self.src_path, shell=True)

tf_cflags, tf_lflags = get_tf_build_flags()

filenames = ['daliop.cc', 'dali_dataset_op.cc']
plugin_src = ''
for filename in filenames:
plugin_src = plugin_src + ' ' + os.path.join(self.src_path, filename)

lib_path = os.path.join(self.plugin_dest_dir, 'libdali_tf_current.so')

# Note: DNDEBUG flag is needed due to issue with TensorFlow custom ops:
# https://github.com/tensorflow/tensorflow/issues/17316
# Do not remove it.
cmd = compiler + ' -Wl,-R,\'$ORIGIN/..\' -std=c++11 -DNDEBUG -shared ' \
+ plugin_src + ' -o ' + lib_path + ' -fPIC ' + dali_cflags + ' ' \
+ tf_cflags + ' ' + cuda_cflags + ' ' + dali_lflags + ' ' + tf_lflags + ' ' \
+ cuda_lflags + ' -O2'
print("Build DALI TF library:\n\n " + cmd + '\n\n')
subprocess.check_call(cmd, cwd=self.src_path, shell=True)

def main():
env = InstallerHelper()
Expand Down
1 change: 1 addition & 0 deletions dali_tf_plugin/nvidia/dali_tf_plugin/dali_tf_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def load_dali_tf_plugin():
if _dali_tf_module is not None:
return _dali_tf_module

import nvidia.dali # Make sure DALI lib is loaded
tf_plugins = glob.glob(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'libdali_tf*.so'))
# Order: 'current', prebuilt for current TF version, prebuilt for other TF versions
tf_version = re.search("(\d+.\d+).\d+", tf.__version__).group(1)
Expand Down
7 changes: 3 additions & 4 deletions docker/Dockerfile_dali_tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ COPY COPYRIGHT .
COPY LICENSE .
COPY VERSION .
COPY cmake ./cmake
COPY include ./include
COPY dali_tf_plugin ./dali_tf_plugin
COPY tools ./tools

COPY dali_tf_plugin/whl whl
RUN pip install whl/*.whl && rm -rf whl

WORKDIR /opt/dali/dali_tf_plugin
WORKDIR /opt/dali/dali_tf_plugin
12 changes: 4 additions & 8 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,13 @@ To access most recent nightly builds please use flowing release channel:

.. code-block:: bash
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/nightly nvidia-dali-nightly-cuda100
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/nightly nvidia-dali-tf-plugin-nightly-cuda100
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/nightly nvidia-dali-nightly-cuda100 nvidia-dali-tf-plugin-nightly-cuda100
* for CUDA 11:

.. code-block:: bash
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/nightly nvidia-dali-nightly-cuda110
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/nightly nvidia-dali-tf-plugin-nightly-cuda110
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/nightly nvidia-dali-nightly-cuda110 nvidia-dali-tf-plugin-nightly-cuda110
Weekly builds
Expand All @@ -164,12 +162,10 @@ builds please use flowing release channel:

.. code-block:: bash
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/weekly nvidia-dali-weekly-cuda100
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/weekly nvidia-dali-tf-plugin-weekly-cuda100
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/weekly nvidia-dali-weekly-cuda100 nvidia-dali-tf-plugin-weekly-cuda100
* for CUDA 11:

.. code-block:: bash
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/weekly nvidia-dali-weekly-cuda110
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/weekly nvidia-dali-tf-plugin-weekly-cuda110
pip install --extra-index-url https://developer.download.nvidia.com/compute/redist/weekly nvidia-dali-weekly-cuda110 nvidia-dali-tf-plugin-weekly-cuda110
72 changes: 72 additions & 0 deletions tools/stubgen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python

# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This is a tool to generate a stub (empty) implenentation of a C API header
# It matches match functions following the pattern:
# DLL_PUBLIC rettype func_name(A a, B b, ...);
# also including linebreaks and extra spaces.
#
# Usage:
# ./stubgen.py path/to/header.h > stub.c

import sys
import re

COPYRIGHT_NOTICE = """
// Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// THIS IS A STUB IMPLEMENTATION IMPLEMENTATION FILE ONLY MEANT FOR BUILD
// PURPOSES.
"""

def stubgen(header_filepath, out_file=sys.stdout):
header_text = ""
with open(header_filepath, "r") as file:
header_text = file.read()

print(COPYRIGHT_NOTICE, file=out_file)
print("#include \"{}\"\n\n".format(header_filepath), file=out_file)

FUNCTION_DECL_PATTERN = "DLL_PUBLIC[\s]+(.*)[\s]+(.*)\(([^\)]*?)\);"
for entry in re.finditer(FUNCTION_DECL_PATTERN, header_text):
ret_type = entry.group(1)
func_name = entry.group(2)
args = entry.group(3)
print('{} {}({}) {{\n}}\n\n'.format(ret_type, func_name, args), file=out_file)


if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Produces an empty stub implementation of a C header')
parser.add_argument('header_filepath', metavar='header', type=str, help='Path to the header file')
parser.add_argument('--output', metavar='output', type=str, help='Path to the output file')
args = parser.parse_args()

f = open(args.output, 'w+') if args.output is not None else sys.stdout
stubgen(args.header_filepath, out_file=f)

0 comments on commit b625495

Please sign in to comment.