Skip to content

Commit

Permalink
Merge pull request #2003 from lissyx/py-win
Browse files Browse the repository at this point in the history
Windows Python bindings
  • Loading branch information
lissyx authored Apr 8, 2019
2 parents 89ed0f4 + 6dbf65c commit 594cf5a
Show file tree
Hide file tree
Showing 20 changed files with 271 additions and 99 deletions.
2 changes: 2 additions & 0 deletions native_client/definitions.mk
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ LINK_PATH_DEEPSPEECH :=
CFLAGS_DEEPSPEECH := -nologo -Fe$(DEEPSPEECH_BIN)
SOX_CFLAGS :=
SOX_LDFLAGS :=
PYTHON_PACKAGES := numpy${NUMPY_BUILD_VERSION}
endif

ifeq ($(TARGET),rpi3)
Expand Down Expand Up @@ -144,6 +145,7 @@ define copy_missing_libs
\
for missing in $$missing_libs; do \
find $(SO_SEARCH) -type f -name "$$missing" -exec cp {} $$TARGET_LIB_DIR \; ; \
chmod +w $$TARGET_LIB_DIR/*.so ; \
if [ ! -z "$$MANIFEST_IN" ]; then \
echo "include $$TARGET_LIB_DIR/$$missing" >> $$MANIFEST_IN; \
fi; \
Expand Down
4 changes: 3 additions & 1 deletion native_client/python/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ MANIFEST.in: bindings-build
> $@
$(call copy_missing_libs,temp_build/deepspeech/*.so,temp_build/deepspeech/lib,$@)

# On Unix, _wrap.o gets generated
# On Windows, _wrap.obj it is
bindings-package: MANIFEST.in
cat MANIFEST.in
rm temp_build/*_wrap.o
rm -f temp_build/*_wrap.o temp_build/Release/*_wrap.obj
AS=$(AS) CC=$(CC) CXX=$(CXX) LD=$(LD) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS_NEEDED) $(RPATH_PYTHON)" MODEL_LDFLAGS="$(LDFLAGS_DIRS)" MODEL_LIBS="$(LIBS)" $(PYTHON_PATH) $(NUMPY_INCLUDE) python ./setup.py bdist_wheel $(PYTHON_PLATFORM_NAME) $(SETUP_FLAGS)

bindings: bindings-build bindings-package
10 changes: 10 additions & 0 deletions native_client/python/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import os
import platform

# On Windows, we can't rely on RPATH being set to $ORIGIN/lib/ or on
# @loader_path/lib but we can change the PATH to include the proper directory
# for the dynamic linker
if platform.system().lower() == "windows":
dslib_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')
os.environ['PATH'] = dslib_path + ';' + os.environ['PATH']

import deepspeech

# rename for backwards compatibility
Expand Down
2 changes: 1 addition & 1 deletion taskcluster/.shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ nodejs:
prep_10: 'nvm install 10.12.0 && nvm use 10.12.0'
prep_11: 'nvm install 11.0.0 && nvm use 11.0.0'
win:
sox: '/usr/bin/wget.exe https://sourceforge.net/projects/sox/files/sox/14.4.2/sox-14.4.2-win32.zip/download -O sox-14.4.2-win32.zip && ""C:\Program Files\7-zip\7z.exe"" x -o$TASKCLUSTER_TASK_DIR/bin/ -tzip -aoa sox-14.4.2-win32.zip && rm sox-*zip && export PATH=$TASKCLUSTER_TASK_DIR/bin/sox-14.4.2/:$PATH'
prep_4: '/usr/bin/wget.exe https://nodejs.org/dist/v4.9.1/node-v4.9.1-win-x64.zip && ""C:\Program Files\7-zip\7z.exe"" x -o$TASKCLUSTER_NODE_DIR -tzip -aoa node-v4.9.1-win-x64.zip && rm node-*.zip && export PATH=$TASKCLUSTER_TASK_DIR/bin/node-v4.9.1-win-x64/:$PATH'
prep_6: '/usr/bin/wget.exe https://nodejs.org/dist/v6.14.4/node-v6.14.4-win-x64.zip && ""C:\Program Files\7-zip\7z.exe"" x -o$TASKCLUSTER_NODE_DIR -tzip -aoa node-v6.14.4-win-x64.zip && rm node-*.zip && export PATH=$TASKCLUSTER_TASK_DIR/bin/node-v6.14.4-win-x64/:$PATH'
prep_7: '/usr/bin/wget.exe https://nodejs.org/dist/v7.10.1/node-v7.10.1-win-x64.zip && ""C:\Program Files\7-zip\7z.exe"" x -o$TASKCLUSTER_NODE_DIR -tzip -aoa node-v7.10.1-win-x64.zip && rm node-*.zip && export PATH=$TASKCLUSTER_TASK_DIR/bin/node-v7.10.1-win-x64/:$PATH'
Expand Down Expand Up @@ -91,6 +90,7 @@ system:
linux: '/home/build-user'
osx: '/Users/build-user'
win: '/c/builds/tc-workdir'
sox_win: '/usr/bin/wget.exe https://sourceforge.net/projects/sox/files/sox/14.4.2/sox-14.4.2-win32.zip/download -O sox-14.4.2-win32.zip && ""C:\Program Files\7-zip\7z.exe"" x -o$TASKCLUSTER_TASK_DIR/bin/ -tzip -aoa sox-14.4.2-win32.zip && rm sox-*zip && export PATH=$TASKCLUSTER_TASK_DIR/bin/sox-14.4.2/:$PATH'
notifications:
irc: '#machinelearning'
aptEc2Mirrors: 'echo "deb http://archive.ubuntu.com/ubuntu/ trusty-updates main" > /etc/apt/sources.list.d/trusty-updates.list && sed -ri -e "s|archive.ubuntu.com|${TASKCLUSTER_WORKER_GROUP}.ec2.archive.ubuntu.com|g" -e "s|security.ubuntu.com|${TASKCLUSTER_WORKER_GROUP}.ec2.archive.ubuntu.com|g" /etc/apt/sources.list && apt-get -qq update && apt-get -qq -y upgrade'
2 changes: 1 addition & 1 deletion taskcluster/test-nodejs_10x-win-amd64-opt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${nodejs.win.sox} && ${nodejs.win.prep_10}
${system.sox_win} && ${nodejs.win.prep_10}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-node-tests.sh 10.x"
metadata:
Expand Down
2 changes: 1 addition & 1 deletion taskcluster/test-nodejs_11x-win-amd64-opt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${nodejs.win.sox} && ${nodejs.win.prep_11}
${system.sox_win} && ${nodejs.win.prep_11}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-node-tests.sh 11.x"
metadata:
Expand Down
2 changes: 1 addition & 1 deletion taskcluster/test-nodejs_4x-win-amd64-opt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${nodejs.win.sox} && ${nodejs.win.prep_4}
${system.sox_win} && ${nodejs.win.prep_4}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-node-tests.sh 4.x"
metadata:
Expand Down
2 changes: 1 addition & 1 deletion taskcluster/test-nodejs_6x-win-amd64-opt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${nodejs.win.sox} && ${nodejs.win.prep_6}
${system.sox_win} && ${nodejs.win.prep_6}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-node-tests.sh 6.x"
metadata:
Expand Down
2 changes: 1 addition & 1 deletion taskcluster/test-nodejs_7x-win-amd64-opt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${nodejs.win.sox} && ${nodejs.win.prep_7}
${system.sox_win} && ${nodejs.win.prep_7}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-node-tests.sh 7.x"
metadata:
Expand Down
2 changes: 1 addition & 1 deletion taskcluster/test-nodejs_8x-win-amd64-opt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${nodejs.win.sox} && ${nodejs.win.prep_8}
${system.sox_win} && ${nodejs.win.prep_8}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-node-tests.sh 8.x"
metadata:
Expand Down
2 changes: 1 addition & 1 deletion taskcluster/test-nodejs_9x-win-amd64-opt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build:
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${nodejs.win.sox} && ${nodejs.win.prep_9}
${system.sox_win} && ${nodejs.win.prep_9}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-node-tests.sh 9.x"
metadata:
Expand Down
13 changes: 13 additions & 0 deletions taskcluster/test-python_35-win-amd64-opt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
build:
template_file: test-win-opt-base.tyml
dependencies:
- "win-amd64-cpu-opt"
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${system.sox_win}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-python-tests.sh 3.5.4:m"
metadata:
name: "DeepSpeech Windows AMD64 CPU Python v3.5 tests"
description: "Testing DeepSpeech for Windows/AMD64 on Python v3.5, CPU only, optimized version"
13 changes: 13 additions & 0 deletions taskcluster/test-python_36-win-amd64-opt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
build:
template_file: test-win-opt-base.tyml
dependencies:
- "win-amd64-cpu-opt"
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${system.sox_win}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-python-tests.sh 3.6.7:m"
metadata:
name: "DeepSpeech Windows AMD64 CPU Python v3.6 tests"
description: "Testing DeepSpeech for Windows/AMD64 on Python v3.6, CPU only, optimized version"
13 changes: 13 additions & 0 deletions taskcluster/test-python_37-win-amd64-opt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
build:
template_file: test-win-opt-base.tyml
dependencies:
- "win-amd64-cpu-opt"
- "test-training_upstream-linux-amd64-py27mu-opt"
system_setup:
>
${system.sox_win}
args:
tests_cmdline: "${system.homedir.win}/DeepSpeech/ds/tc-python-tests.sh 3.7.1:m"
metadata:
name: "DeepSpeech Windows AMD64 CPU Python v3.7 tests"
description: "Testing DeepSpeech for Windows/AMD64 on Python v3.7, CPU only, optimized version"
4 changes: 4 additions & 0 deletions taskcluster/win-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export PATH=$PATH:$(cygpath ${ChocolateyInstall})/bin:'/c/Program Files/nodejs/'

do_deepspeech_binary_build

# Those are the versions available on NuGet.org
export SUPPORTED_PYTHON_VERSIONS="3.5.4 3.6.7 3.7.1"
do_deepspeech_python_build "${cuda}"

do_deepspeech_nodejs_build "${cuda}"

do_deepspeech_netframework_build
Expand Down
4 changes: 4 additions & 0 deletions taskcluster/win-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ cp ${DS_ROOT_TASK}/DeepSpeech/ds/examples/net_framework/CSharpExamples/*.nupkg $

cp ${DS_ROOT_TASK}/DeepSpeech/ds/examples/net_framework/CSharpExamples/DeepSpeechConsole/bin/x64/Release/DeepSpeechConsole.exe ${TASKCLUSTER_ARTIFACTS}/

if [ -d ${DS_ROOT_TASK}/DeepSpeech/ds/wheels ]; then
cp ${DS_ROOT_TASK}/DeepSpeech/ds/wheels/* ${TASKCLUSTER_ARTIFACTS}/
fi;

if [ -f ${DS_ROOT_TASK}/DeepSpeech/ds/native_client/javascript/wrapper.tar.gz ]; then
cp ${DS_ROOT_TASK}/DeepSpeech/ds/native_client/javascript/wrapper.tar.gz ${TASKCLUSTER_ARTIFACTS}/
cp ${DS_ROOT_TASK}/DeepSpeech/ds/native_client/javascript/deepspeech-*.tgz ${TASKCLUSTER_ARTIFACTS}/
Expand Down
36 changes: 9 additions & 27 deletions tc-python-tests-prod.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,7 @@ set -xe

source $(dirname "$0")/tc-tests-utils.sh

pyver_full=$1

if [ -z "${pyver_full}" ]; then
echo "No python version given, aborting."
exit 1
fi;

pyver=$(echo "${pyver_full}" | cut -d':' -f1)

# 2.7.x => 27
pyver_pkg=$(echo "${pyver}" | cut -d'.' -f1,2 | tr -d '.')

py_unicode_type=$(echo "${pyver_full}" | cut -d':' -f2)
if [ "${py_unicode_type}" = "m" ]; then
pyconf="ucs2"
elif [ "${py_unicode_type}" = "mu" ]; then
pyconf="ucs4"
fi;
extract_python_versions "$1" "pyver" "pyver_pkg" "py_unicode_type" "pyconf"

unset PYTHON_BIN_PATH
unset PYTHONPATH
Expand All @@ -43,18 +26,17 @@ install_pyenv_virtualenv "$(pyenv root)/plugins/pyenv-virtualenv"

maybe_ssl102_py37 ${pyver}

maybe_numpy_min_version_winamd64 ${pyver}

PYENV_NAME=deepspeech-test
LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH PYTHON_CONFIGURE_OPTS="--enable-unicode=${pyconf} ${PY37_OPENSSL} ${EXTRA_PYTHON_CONFIGURE_OPTS}" pyenv install ${pyver}
pyenv virtualenv ${pyver} ${PYENV_NAME}
source ${PYENV_ROOT}/versions/${pyver}/envs/${PYENV_NAME}/bin/activate
LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH PYTHON_CONFIGURE_OPTS="--enable-unicode=${pyconf} ${PY37_OPENSSL} ${EXTRA_PYTHON_CONFIGURE_OPTS}" pyenv_install ${pyver}

platform=$(python -c 'import sys; import platform; plat = platform.system().lower(); arch = platform.machine().lower(); plat = "manylinux1" if plat == "linux" and arch == "x86_64" else plat; plat = "macosx_10_10" if plat == "darwin" else plat; sys.stdout.write("%s_%s" % (plat, platform.machine()));')
whl_ds_version="$(python -c 'from pkg_resources import parse_version; print(parse_version("'${DS_VERSION}'"))')"
deepspeech_pkg="deepspeech-${whl_ds_version}-cp${pyver_pkg}-cp${pyver_pkg}${py_unicode_type}-${platform}.whl"
setup_pyenv_virtualenv "${pyver}" "${PYENV_NAME}"
virtualenv_activate "${pyver}" "${PYENV_NAME}"

LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH pip install --verbose --only-binary :all: ${PY37_SOURCE_PACKAGE} --upgrade ${DEEPSPEECH_ARTIFACTS_ROOT}/${deepspeech_pkg} | cat
deepspeech_pkg_url=$(get_python_pkg_url ${pyver_pkg} ${py_unicode_type})
LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH pip install --verbose --only-binary :all: ${PY37_SOURCE_PACKAGE} --upgrade ${deepspeech_pkg_url} | cat

run_prod_inference_tests

deactivate
pyenv uninstall --force ${PYENV_NAME}
virtualenv_deactivate "${pyver}" "${PYENV_NAME}"
38 changes: 11 additions & 27 deletions tc-python-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,7 @@ set -xe

source $(dirname "$0")/tc-tests-utils.sh

pyver_full=$1

if [ -z "${pyver_full}" ]; then
echo "No python version given, aborting."
exit 1
fi;

pyver=$(echo "${pyver_full}" | cut -d':' -f1)

# 2.7.x => 27
pyver_pkg=$(echo "${pyver}" | cut -d'.' -f1,2 | tr -d '.')

py_unicode_type=$(echo "${pyver_full}" | cut -d':' -f2)
if [ "${py_unicode_type}" = "m" ]; then
pyconf="ucs2"
elif [ "${py_unicode_type}" = "mu" ]; then
pyconf="ucs4"
fi;
extract_python_versions "$1" "pyver" "pyver_pkg" "py_unicode_type" "pyconf"

unset PYTHON_BIN_PATH
unset PYTHONPATH
Expand All @@ -37,19 +20,20 @@ install_pyenv_virtualenv "$(pyenv root)/plugins/pyenv-virtualenv"

maybe_ssl102_py37 ${pyver}

maybe_numpy_min_version_winamd64 ${pyver}

PYENV_NAME=deepspeech-test
LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH PYTHON_CONFIGURE_OPTS="--enable-unicode=${pyconf} ${PY37_OPENSSL} ${EXTRA_PYTHON_CONFIGURE_OPTS}" pyenv install ${pyver}
pyenv virtualenv ${pyver} ${PYENV_NAME}
source ${PYENV_ROOT}/versions/${pyver}/envs/${PYENV_NAME}/bin/activate
LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH PYTHON_CONFIGURE_OPTS="--enable-unicode=${pyconf} ${PY37_OPENSSL} ${EXTRA_PYTHON_CONFIGURE_OPTS}" pyenv_install ${pyver}

platform=$(python -c 'import sys; import platform; plat = platform.system().lower(); arch = platform.machine().lower(); plat = "manylinux1" if plat == "linux" and arch == "x86_64" else plat; plat = "macosx_10_10" if plat == "darwin" else plat; sys.stdout.write("%s_%s" % (plat, platform.machine()));')
whl_ds_version="$(python -c 'from pkg_resources import parse_version; print(parse_version("'${DS_VERSION}'"))')"
deepspeech_pkg="deepspeech-${whl_ds_version}-cp${pyver_pkg}-cp${pyver_pkg}${py_unicode_type}-${platform}.whl"
setup_pyenv_virtualenv "${pyver}" "${PYENV_NAME}"
virtualenv_activate "${pyver}" "${PYENV_NAME}"

deepspeech_pkg_url=${DEEPSPEECH_ARTIFACTS_ROOT}/${deepspeech_pkg}
deepspeech_pkg_url=$(get_python_pkg_url ${pyver_pkg} ${py_unicode_type})
LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH pip install --verbose --only-binary :all: ${PY37_SOURCE_PACKAGE} --upgrade ${deepspeech_pkg_url} | cat

which deepspeech
deepspeech --version

run_all_inference_tests

deactivate
pyenv uninstall --force ${PYENV_NAME}
virtualenv_deactivate "${pyver}" "${PYENV_NAME}"
37 changes: 8 additions & 29 deletions tc-single-shot-inference.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,11 @@ set -xe

source $(dirname "$0")/tc-tests-utils.sh

pyver_full=$1
extract_python_versions "$1" "pyver" "pyver_pkg" "py_unicode_type" "pyconf"

ds=$2
frozen=$2

if [ -z "${pyver_full}" ]; then
echo "No python version given, aborting."
exit 1
fi;

pyver=$(echo "${pyver_full}" | cut -d':' -f1)

# 2.7.x => 27
pyver_pkg=$(echo "${pyver}" | cut -d'.' -f1,2 | tr -d '.')

py_unicode_type=$(echo "${pyver_full}" | cut -d':' -f2)
if [ "${py_unicode_type}" = "m" ]; then
pyconf="ucs2"
elif [ "${py_unicode_type}" = "mu" ]; then
pyconf="ucs4"
fi;

unset PYTHON_BIN_PATH
unset PYTHONPATH
export PYENV_ROOT="${HOME}/ds-train/.pyenv"
Expand All @@ -39,23 +23,18 @@ install_pyenv "${PYENV_ROOT}"
install_pyenv_virtualenv "$(pyenv root)/plugins/pyenv-virtualenv"

PYENV_NAME=deepspeech-train
PYTHON_CONFIGURE_OPTS="--enable-unicode=${pyconf}" pyenv install ${pyver}
pyenv virtualenv ${pyver} ${PYENV_NAME}
source ${PYENV_ROOT}/versions/${pyver}/envs/${PYENV_NAME}/bin/activate

pip install --upgrade -r ${HOME}/DeepSpeech/ds/requirements.txt | cat
PYTHON_CONFIGURE_OPTS="--enable-unicode=${pyconf}" pyenv_install ${pyver}

platform=$(python -c 'import sys; import platform; plat = platform.system().lower(); arch = platform.machine().lower(); plat = "manylinux1" if plat == "linux" and arch == "x86_64" else plat; plat = "macosx_10_10" if plat == "darwin" else plat; sys.stdout.write("%s_%s" % (plat, platform.machine()));')
whl_ds_version="$(python -c 'from pkg_resources import parse_version; print(parse_version("'${DS_VERSION}'"))')"
decoder_pkg="ds_ctcdecoder-${whl_ds_version}-cp${pyver_pkg}-cp${pyver_pkg}${py_unicode_type}-${platform}.whl"
setup_pyenv_virtualenv "${pyver}" "${PYENV_NAME}"
virtualenv_activate "${pyver}" "${PYENV_NAME}"

decoder_pkg_url=${DECODER_ARTIFACTS_ROOT}/${decoder_pkg}
pip install --upgrade -r ${HOME}/DeepSpeech/ds/requirements.txt | cat

decoder_pkg_url=$(get_python_pkg_url ${pyver_pkg} ${py_unicode_type} "ds_ctcdecoder" "${DECODER_ARTIFACTS_ROOT}")
LD_LIBRARY_PATH=${PY37_LDPATH}:$LD_LIBRARY_PATH pip install --verbose --only-binary :all: ${PY37_SOURCE_PACKAGE} --upgrade ${decoder_pkg_url} | cat

pushd ${HOME}/DeepSpeech/ds/
time ./bin/run-tc-ldc93s1_singleshotinference.sh
popd

deactivate
pyenv uninstall --force ${PYENV_NAME}
virtualenv_deactivate "${pyver}" "${PYENV_NAME}"
Loading

0 comments on commit 594cf5a

Please sign in to comment.