summaryrefslogtreecommitdiff
path: root/infra/packaging
diff options
context:
space:
mode:
Diffstat (limited to 'infra/packaging')
-rw-r--r--infra/packaging/build95
-rw-r--r--infra/packaging/chklist/LAYOUT_19111547
-rw-r--r--infra/packaging/chklist/LAYOUT_19121539
-rw-r--r--infra/packaging/chklist/TF2CIRCLE_EXIST13
-rw-r--r--infra/packaging/chklist/TF2CIRCLE_RUNNABLE16
-rw-r--r--infra/packaging/chklist/TF2NNPKG_EXIST13
-rw-r--r--infra/packaging/chklist/TF2TFLITE_EXIST13
-rw-r--r--infra/packaging/chklist/TF2TFLITE_RUNNABLE13
-rw-r--r--infra/packaging/preset/2019111549
-rw-r--r--infra/packaging/preset/2019121542
-rw-r--r--infra/packaging/preset/20191231_windows51
-rw-r--r--infra/packaging/preset/20200115_windows50
-rw-r--r--infra/packaging/res/tf2nnpkg87
-rw-r--r--infra/packaging/res/tf2nnpkg.2019121568
-rw-r--r--infra/packaging/res/tflite_schema.fbs698
-rw-r--r--infra/packaging/verify82
16 files changed, 1376 insertions, 0 deletions
diff --git a/infra/packaging/build b/infra/packaging/build
new file mode 100644
index 000000000..81ab2ec49
--- /dev/null
+++ b/infra/packaging/build
@@ -0,0 +1,95 @@
+#!/bin/bash
+
+SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+if [[ -z "${NNAS_PROJECT_PATH}" ]]; then
+ echo "ERROR: NNAS_PROJECT_PATH is not specified"
+ exit 255
+fi
+
+# The default preset
+PRESET="20191215"
+
+EXTRA_OPTIONS=()
+while [ "$#" -ne 0 ]; do
+ CUR="$1"
+
+ case $CUR in
+ '--prefix')
+ NNAS_INSTALL_PREFIX="$2"
+ shift 2
+ ;;
+ '--preset')
+ PRESET="$2"
+ shift 2
+ ;;
+ '--')
+ shift
+ while [ "$#" -ne 0 ]; do
+ EXTRA_OPTIONS+=("$1")
+ shift
+ done
+ ;;
+ *)
+ echo "ERROR: '${CUR}' is invalid"
+ exit 255
+ ;;
+ esac
+done
+
+# Q. Is it better to have the default value for NNAS_INSTALL_PREFIX?
+# TODO Show USAGE
+if [[ -z "${NNAS_INSTALL_PREFIX}" ]]; then
+ echo "ERROR: --prefix is not specified"
+ exit 255
+fi
+
+PRESET_PATH="${SCRIPT_PATH}/preset/${PRESET}"
+
+if [[ ! -f "${PRESET_PATH}" ]]; then
+ echo "ERROR: ${PRESET} is unavailable"
+ # TODO Show available presets
+ exit 255
+fi
+
+echo "-- Use '${PRESET}' SDK preset"
+
+source "${PRESET_PATH}"
+
+# Normalize to absolute path
+if [[ "${NNAS_INSTALL_PREFIX}" != /* ]]; then
+ NNAS_INSTALL_PREFIX=${PWD}/${NNAS_INSTALL_PREFIX}
+fi
+
+if [[ -z "${NNAS_BUILD_PREFIX}" ]]; then
+ # Create a temporary directory and use it!
+ NNAS_BUILD_PREFIX=$(mktemp -d)
+ trap "{ rm -rf $NNAS_BUILD_PREFIX; }" EXIT
+fi
+
+# Create a release directory
+mkdir -p "${NNAS_INSTALL_PREFIX}"
+
+# Build and Install NNCC
+NNCC_BUILD_PREFIX="${NNAS_BUILD_PREFIX}/nncc"
+NNCC_INSTALL_PREFIX="${NNAS_INSTALL_PREFIX}"
+
+mkdir -p "${NNCC_BUILD_PREFIX}"
+cd "${NNCC_BUILD_PREFIX}"
+
+function join_by
+{
+ local IFS="$1"; shift; echo "$*"
+}
+
+# Invoke "preset_configure" function that the preset provides
+preset_configure
+
+NPROC=$(cat /proc/cpuinfo | grep -c processor)
+cmake --build . -- -j$((NPROC/2)) all
+cmake --build . -- install
+# Install NN Package tools
+NNPKG_INSTALL_PREFIX="${NNAS_INSTALL_PREFIX}"
+
+# Invoke "preset_install" function that the preset provides
+preset_install
diff --git a/infra/packaging/chklist/LAYOUT_191115 b/infra/packaging/chklist/LAYOUT_191115
new file mode 100644
index 000000000..e041a2c80
--- /dev/null
+++ b/infra/packaging/chklist/LAYOUT_191115
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Check whether the package has the following layout:
+#
+# bin/
+# model2nnpkg.sh
+# tf2circle
+# tf2nnpkg
+# tf2tflite
+# tflite2circle.sh
+# tflitejson2circlejson.py
+# lib/
+# libexo.so
+# liblocoex_customop.so
+# libloco.so
+# libmoco_import.so
+# libmoco_lang.so
+# libmoco_log.so
+# libmoco_pass.so
+# libmoco_service.so
+# libmoco_support.so
+# libmoco_tf_frontend.so
+# res/
+# circle_schema.fbs
+# tflite_schema.fbs
+
+function prepare()
+{
+ export QUESTION="Is compatible with the 2019/11/15 layout?"
+}
+
+function run()
+{
+ # The result of running "find . -print | sort | tr -d '\n\0'" from the expected package
+ EXPECTED="."
+ EXPECTED+="./bin./bin/model2nnpkg.sh./bin/tf2circle./bin/tf2nnpkg./bin/tf2tflite./bin/tflite2circle.sh"
+ EXPECTED+="./bin/tflitejson2circlejson.py./lib./lib/libexo.so./lib/liblocoex_customop.so./lib/libloco.so"
+ EXPECTED+="./lib/libmoco_import.so./lib/libmoco_lang.so./lib/libmoco_log.so./lib/libmoco_pass.so"
+ EXPECTED+="./lib/libmoco_service.so./lib/libmoco_support.so./lib/libmoco_tf_frontend.so./res"
+ EXPECTED+="./res/circle_schema.fbs./res/tflite_schema.fbs"
+
+ OBTAINED=$(cd "${NNAS_INSTALL_PREFIX}" && find . -print | sort | tr -d '\n\0')
+
+ if [[ "${OBTAINED}" = "${EXPECTED}" ]]; then
+ export PASSED=1
+ fi
+}
diff --git a/infra/packaging/chklist/LAYOUT_191215 b/infra/packaging/chklist/LAYOUT_191215
new file mode 100644
index 000000000..8095197f6
--- /dev/null
+++ b/infra/packaging/chklist/LAYOUT_191215
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# Check whether the package has the following layout:
+#
+# bin/
+# model2nnpkg.sh
+# tf2circle
+# tf2nnpkg
+# lib/
+# libexo.so
+# liblocoex_customop.so
+# libloco.so
+# libmoco_import.so
+# libmoco_lang.so
+# libmoco_log.so
+# libmoco_pass.so
+# libmoco_service.so
+# libmoco_support.so
+# libmoco_tf_frontend.so
+
+function prepare()
+{
+ export QUESTION="Is compatible with the 2019/12/15 layout?"
+}
+
+function run()
+{
+ # The result of running "find . -print | sort | tr -d '\n\0'" from the expected package
+ EXPECTED="."
+ EXPECTED+="./bin./bin/model2nnpkg.sh./bin/tf2circle./bin/tf2nnpkg"
+ EXPECTED+="./lib./lib/libexo.so./lib/liblocoex_customop.so"
+ EXPECTED+="./lib/libloco.so./lib/libmoco_import.so./lib/libmoco_lang.so./lib/libmoco_log.so./lib/libmoco_pass.so./lib/libmoco_service.so./lib/libmoco_support.so./lib/libmoco_tf_frontend.so"
+
+ OBTAINED=$(cd "${NNAS_INSTALL_PREFIX}" && find . -print | sort | tr -d '\n\0')
+
+ if [[ "${OBTAINED}" = "${EXPECTED}" ]]; then
+ export PASSED=1
+ fi
+}
diff --git a/infra/packaging/chklist/TF2CIRCLE_EXIST b/infra/packaging/chklist/TF2CIRCLE_EXIST
new file mode 100644
index 000000000..b0834fc27
--- /dev/null
+++ b/infra/packaging/chklist/TF2CIRCLE_EXIST
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+function prepare()
+{
+ export QUESTION="Is there tf2circle?"
+}
+
+function run()
+{
+ if [[ -f "${NNAS_INSTALL_PREFIX}/bin/tf2circle" ]]; then
+ export PASSED=1
+ fi
+}
diff --git a/infra/packaging/chklist/TF2CIRCLE_RUNNABLE b/infra/packaging/chklist/TF2CIRCLE_RUNNABLE
new file mode 100644
index 000000000..597778ff4
--- /dev/null
+++ b/infra/packaging/chklist/TF2CIRCLE_RUNNABLE
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+function prepare()
+{
+ export QUESTION="Is it possible to run tf2circle?"
+}
+
+function run()
+{
+ BIN="${NNAS_INSTALL_PREFIX}/bin/tf2circle"
+ if [[ -f "${BIN}" ]]; then
+ if [[ $(ldd -r "${BIN}" | grep '^undefined' | wc -l) -eq 0 ]]; then
+ export PASSED=1
+ fi
+ fi
+}
diff --git a/infra/packaging/chklist/TF2NNPKG_EXIST b/infra/packaging/chklist/TF2NNPKG_EXIST
new file mode 100644
index 000000000..bbe9a3157
--- /dev/null
+++ b/infra/packaging/chklist/TF2NNPKG_EXIST
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+function prepare()
+{
+ export QUESTION="Is there tf2nnpkg?"
+}
+
+function run()
+{
+ if [[ -f "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg" ]]; then
+ export PASSED=1
+ fi
+}
diff --git a/infra/packaging/chklist/TF2TFLITE_EXIST b/infra/packaging/chklist/TF2TFLITE_EXIST
new file mode 100644
index 000000000..1a1c65cbc
--- /dev/null
+++ b/infra/packaging/chklist/TF2TFLITE_EXIST
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+function prepare()
+{
+ export QUESTION="Is there tf2tflite?"
+}
+
+function run()
+{
+ if [[ -f "${NNAS_INSTALL_PREFIX}/bin/tf2tflite" ]]; then
+ export PASSED=1
+ fi
+}
diff --git a/infra/packaging/chklist/TF2TFLITE_RUNNABLE b/infra/packaging/chklist/TF2TFLITE_RUNNABLE
new file mode 100644
index 000000000..4c1239c33
--- /dev/null
+++ b/infra/packaging/chklist/TF2TFLITE_RUNNABLE
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+function prepare()
+{
+ export QUESTION="Is it possible to run tf2tflite?"
+}
+
+function run()
+{
+ if [[ $(ldd -r "${NNAS_INSTALL_PREFIX}/bin/tf2tflite" | grep '^undefined' | wc -l) -eq 0 ]]; then
+ export PASSED=1
+ fi
+}
diff --git a/infra/packaging/preset/20191115 b/infra/packaging/preset/20191115
new file mode 100644
index 000000000..e2f6f8c73
--- /dev/null
+++ b/infra/packaging/preset/20191115
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+function preset_configure()
+{
+ REQUIRED_UNITS=()
+ # Common Libraries
+ REQUIRED_UNITS+=("angkor" "bino" "cwrap" "fipe" "pepper-str" "pepper-strcast" "pp" "stdex")
+ REQUIRED_UNITS+=("oops" "pepper-assert")
+ # Hermes Logging Framework
+ REQUIRED_UNITS+=("hermes" "hermes-std")
+ # loco IR and related utilities
+ REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo")
+ # loco IR extension: Custom Op Support
+ REQUIRED_UNITS+=("locoex-customop")
+ # TensorFlow Libraries
+ REQUIRED_UNITS+=("tfinfo" "plier-tf")
+ # TensorFlow GraphDef I/O
+ REQUIRED_UNITS+=("mio-tf")
+ # TensorFlow Frontend (.pb/.pbtxt -> loco.caninical)
+ REQUIRED_UNITS+=("moco-log" "moco" "moco-tf")
+ # TensorFlow Lite/Circle Backend (loco.canonical -> .tflite, loco.circle -> .circle)
+ REQUIRED_UNITS+=("exo")
+ # Tools
+ REQUIRED_UNITS+=("tf2tflite" "tf2circle")
+
+ # TODO Use "nncc configure" and "nncc build"
+ cmake \
+ -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \
+ -DCMAKE_BUILD_TYPE=release \
+ -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \
+ ${EXTRA_OPTIONS[@]} \
+ "${NNAS_PROJECT_PATH}/infra/nncc"
+}
+
+function preset_install()
+{
+ install -t "${NNPKG_INSTALL_PREFIX}/bin" -D \
+ "${NNAS_PROJECT_PATH}/tools/nnpackage_tool/model2nnpkg/model2nnpkg.sh" \
+ "${NNAS_PROJECT_PATH}/tools/nnpackage_tool/tflite2circle/tflite2circle.sh" \
+ "${NNAS_PROJECT_PATH}/tools/nnpackage_tool/tflite2circle/tflitejson2circlejson.py"
+
+ install -T -m 644 -D \
+ "${SCRIPT_PATH}/res/tflite_schema.fbs" "${NNPKG_INSTALL_PREFIX}/res/tflite_schema.fbs"
+ install -T -m 644 -D \
+ "${NNAS_PROJECT_PATH}/nnpackage/schema/circle_schema.fbs" "${NNPKG_INSTALL_PREFIX}/res/circle_schema.fbs"
+
+ # Install tf2nnpkg
+ install -T -m 755 -D "${SCRIPT_PATH}/res/tf2nnpkg" "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg"
+}
diff --git a/infra/packaging/preset/20191215 b/infra/packaging/preset/20191215
new file mode 100644
index 000000000..980d729fc
--- /dev/null
+++ b/infra/packaging/preset/20191215
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+function preset_configure()
+{
+ REQUIRED_UNITS=()
+ # Common Libraries
+ REQUIRED_UNITS+=("angkor" "bino" "cwrap" "fipe" "pepper-str" "pepper-strcast" "pp" "stdex")
+ REQUIRED_UNITS+=("oops" "pepper-assert")
+ # Hermes Logging Framework
+ REQUIRED_UNITS+=("hermes" "hermes-std")
+ # loco IR and related utilities
+ REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo")
+ # loco IR extension: Custom Op Support
+ REQUIRED_UNITS+=("locoex-customop")
+ # TensorFlow Libraries
+ REQUIRED_UNITS+=("tfinfo" "plier-tf")
+ # TensorFlow GraphDef I/O
+ REQUIRED_UNITS+=("mio-tf")
+ # TensorFlow Frontend (.pb/.pbtxt -> loco.caninical)
+ REQUIRED_UNITS+=("moco-log" "moco" "moco-tf")
+ # TensorFlow Lite/Circle Backend (loco.canonical -> .tflite, loco.circle -> .circle)
+ REQUIRED_UNITS+=("exo")
+ # Tools
+ REQUIRED_UNITS+=("tf2circle")
+
+ # TODO Use "nncc configure" and "nncc build"
+ cmake \
+ -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \
+ -DCMAKE_BUILD_TYPE=release \
+ -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \
+ ${EXTRA_OPTIONS[@]} \
+ "${NNAS_PROJECT_PATH}/infra/nncc"
+}
+
+function preset_install()
+{
+ install -t "${NNPKG_INSTALL_PREFIX}/bin" -D \
+ "${NNAS_PROJECT_PATH}/tools/nnpackage_tool/model2nnpkg/model2nnpkg.sh"
+
+ # Install tf2nnpkg
+ install -T -m 755 -D "${SCRIPT_PATH}/res/tf2nnpkg.20191215" "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg"
+}
diff --git a/infra/packaging/preset/20191231_windows b/infra/packaging/preset/20191231_windows
new file mode 100644
index 000000000..7b511196f
--- /dev/null
+++ b/infra/packaging/preset/20191231_windows
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+function preset_configure()
+{
+ REQUIRED_UNITS=()
+ # Common Libraries
+ REQUIRED_UNITS+=("angkor" "bino" "cwrap" "fipe" "pepper-str" "pepper-strcast" "pp" "stdex")
+ REQUIRED_UNITS+=("oops" "pepper-assert")
+ # Hermes Logging Framework
+ REQUIRED_UNITS+=("hermes" "hermes-std")
+ # loco IR and related utilities
+ REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo")
+ # loco IR extension: Custom Op Support
+ REQUIRED_UNITS+=("locoex-customop")
+ # TensorFlow Libraries
+ REQUIRED_UNITS+=("tfinfo" "plier-tf")
+ # TensorFlow GraphDef I/O
+ REQUIRED_UNITS+=("mio-tf")
+ # TensorFlow Frontend (.pb/.pbtxt -> loco.caninical)
+ REQUIRED_UNITS+=("moco-log" "moco" "moco-tf")
+ # TensorFlow Lite/Circle Backend (loco.canonical -> .tflite, loco.circle -> .circle)
+ REQUIRED_UNITS+=("exo")
+ # Tools
+ REQUIRED_UNITS+=("tf2circle")
+
+ # TODO Use "nncc configure" and "nncc build"
+ cmake \
+ -G "MSYS Makefiles" \
+ -DUSE_PROTOBUF_LEGACY_IMPORT=ON \
+ -DCMAKE_EXE_LINKER_FLAGS="-Wl,--allow-multiple-definition" \
+ -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--allow-multiple-definition" \
+ -DENABLE_TEST=OFF \
+ -DDOWNLOAD_GTEST=OFF \
+ -DBUILD_GTEST=OFF \
+ -DCMAKE_C_COMPILER=gcc \
+ -DCMAKE_CXX_COMPILER=g++ \
+ -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \
+ -DCMAKE_BUILD_TYPE=release \
+ -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \
+ ${EXTRA_OPTIONS[@]} \
+ "${NNAS_PROJECT_PATH}/infra/nncc"
+}
+
+function preset_install()
+{
+ install -t "${NNPKG_INSTALL_PREFIX}/bin" -D \
+ "${NNAS_PROJECT_PATH}/tools/nnpackage_tool/model2nnpkg/model2nnpkg.sh"
+
+ # Install tf2nnpkg
+ install -T -m 755 -D "${SCRIPT_PATH}/res/tf2nnpkg.20191215" "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg"
+}
diff --git a/infra/packaging/preset/20200115_windows b/infra/packaging/preset/20200115_windows
new file mode 100644
index 000000000..da349bb3a
--- /dev/null
+++ b/infra/packaging/preset/20200115_windows
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+function preset_configure()
+{
+ REQUIRED_UNITS=()
+ # Common Libraries
+ REQUIRED_UNITS+=("angkor" "bino" "cwrap" "fipe" "pepper-str" "pepper-strcast" "pp" "stdex")
+ REQUIRED_UNITS+=("oops" "pepper-assert")
+ # Hermes Logging Framework
+ REQUIRED_UNITS+=("hermes" "hermes-std")
+ # loco IR and related utilities
+ REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo")
+ # loco IR extension: Custom Op Support
+ REQUIRED_UNITS+=("locoex-customop")
+ # TensorFlow Libraries
+ REQUIRED_UNITS+=("tfinfo" "plier-tf")
+ # TensorFlow GraphDef I/O
+ REQUIRED_UNITS+=("mio-tf")
+ # TensorFlow Frontend (.pb/.pbtxt -> loco.caninical)
+ REQUIRED_UNITS+=("moco-log" "moco" "moco-tf")
+ # TensorFlow Lite/Circle Backend (loco.canonical -> .tflite, loco.circle -> .circle)
+ REQUIRED_UNITS+=("exo")
+ # Tools
+ REQUIRED_UNITS+=("tf2nnpkg")
+
+ # TODO Use "nncc configure" and "nncc build"
+ cmake \
+ -G "MSYS Makefiles" \
+ -DTF2NNPKG_FOR_WINDOWS=ON \
+ -DUSE_PROTOBUF_LEGACY_IMPORT=ON \
+ -DCMAKE_EXE_LINKER_FLAGS="-Wl,--allow-multiple-definition" \
+ -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--allow-multiple-definition" \
+ -DENABLE_TEST=OFF \
+ -DDOWNLOAD_GTEST=OFF \
+ -DBUILD_GTEST=OFF \
+ -DCMAKE_C_COMPILER=gcc \
+ -DCMAKE_CXX_COMPILER=g++ \
+ -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \
+ -DCMAKE_BUILD_TYPE=release \
+ -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \
+ ${EXTRA_OPTIONS[@]} \
+ "${NNAS_PROJECT_PATH}/infra/nncc"
+}
+
+function preset_install()
+{
+ # Install libraries to bin/ for Windows release
+ mv ${NNCC_INSTALL_PREFIX}/lib/*.dll ${NNCC_INSTALL_PREFIX}/bin
+ rm -rf ${NNCC_INSTALL_PREFIX}/lib
+}
diff --git a/infra/packaging/res/tf2nnpkg b/infra/packaging/res/tf2nnpkg
new file mode 100644
index 000000000..c300eb856
--- /dev/null
+++ b/infra/packaging/res/tf2nnpkg
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+set -e
+
+ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+command_exists() {
+ if [ "$#" -le 0 ]; then
+ return 1
+ fi
+ command -v "$@" > /dev/null 2>&1
+}
+
+usage()
+{
+ echo "Usage: tf2nnpkg --info <path/to/info> --graphdef <path/to/pb> -o <path/to/nnpkg/directory>"
+ exit 0
+}
+
+USE_TF2CIRCLE=0
+
+# Parse command-line arguments
+#
+while [ "$#" -ne 0 ]; do
+ CUR="$1"
+
+ case $CUR in
+ '--help')
+ usage
+ ;;
+ '--info')
+ export INFO_FILE="$2"
+ shift 2
+ ;;
+ '--graphdef')
+ export GRAPHDEF_FILE="$2"
+ shift 2
+ ;;
+ '-o')
+ export OUTPUT_DIR="$2"
+ shift 2
+ ;;
+ '--use-tf2circle')
+ USE_TF2CIRCLE=1
+ shift 1
+ ;;
+ *)
+ echo "${CUR}"
+ shift
+ ;;
+ esac
+done
+
+if [ -z ${GRAPHDEF_FILE} ] || [ ! -e ${GRAPHDEF_FILE} ]; then
+ echo "pb is not found. Please check --graphdef is correct."
+ exit 2
+fi
+
+if [ -z ${INFO_FILE} ] || [ ! -e ${INFO_FILE} ]; then
+ echo "info is not found. Please check --info is correct."
+ exit 2
+fi
+
+FILE_BASE=$(basename ${GRAPHDEF_FILE})
+MODEL_NAME="${FILE_BASE%.*}"
+
+if [[ ${USE_TF2CIRCLE} -eq 0 ]]; then
+ export flatc=$(which flatc)
+ export tflite_schema="${ROOT}/res/tflite_schema.fbs"
+ export circle_schema="${ROOT}/res/circle_schema.fbs"
+
+ if ! command_exists $flatc; then
+ echo "Please make sure flatc is in path"
+ exit 2
+ fi
+fi
+
+TMPDIR=$(mktemp -d)
+trap "{ rm -rf $TMPDIR; }" EXIT
+
+if [[ ${USE_TF2CIRCLE} -eq 0 ]]; then
+ "${ROOT}/bin/tf2tflite" "${INFO_FILE}" "${GRAPHDEF_FILE}" "${TMPDIR}/${MODEL_NAME}.tflite"
+ "${ROOT}/bin/tflite2circle.sh" -o "${TMPDIR}" "${TMPDIR}/${MODEL_NAME}.tflite"
+else
+ "${ROOT}/bin/tf2circle" "${INFO_FILE}" "${GRAPHDEF_FILE}" "${TMPDIR}/${MODEL_NAME}.circle"
+fi
+"${ROOT}/bin/model2nnpkg.sh" -o "${OUTPUT_DIR}" "${TMPDIR}/${MODEL_NAME}.circle"
diff --git a/infra/packaging/res/tf2nnpkg.20191215 b/infra/packaging/res/tf2nnpkg.20191215
new file mode 100644
index 000000000..d334a908d
--- /dev/null
+++ b/infra/packaging/res/tf2nnpkg.20191215
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+set -e
+
+ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+command_exists() {
+ if [ "$#" -le 0 ]; then
+ return 1
+ fi
+ command -v "$@" > /dev/null 2>&1
+}
+
+usage()
+{
+ echo "Usage: tf2nnpkg --info <path/to/info> --graphdef <path/to/pb> -o <path/to/nnpkg/directory>"
+ exit 0
+}
+
+# Parse command-line arguments
+#
+while [ "$#" -ne 0 ]; do
+ CUR="$1"
+
+ case $CUR in
+ '--help')
+ usage
+ ;;
+ '--info')
+ export INFO_FILE="$2"
+ shift 2
+ ;;
+ '--graphdef')
+ export GRAPHDEF_FILE="$2"
+ shift 2
+ ;;
+ '-o')
+ export OUTPUT_DIR="$2"
+ shift 2
+ ;;
+ '--use-tf2circle')
+ echo "WARNING! --use-tf2circle is deprecated"
+ shift 1
+ ;;
+ *)
+ echo "${CUR}"
+ shift
+ ;;
+ esac
+done
+
+if [ -z ${GRAPHDEF_FILE} ] || [ ! -e ${GRAPHDEF_FILE} ]; then
+ echo "pb is not found. Please check --graphdef is correct."
+ exit 2
+fi
+
+if [ -z ${INFO_FILE} ] || [ ! -e ${INFO_FILE} ]; then
+ echo "info is not found. Please check --info is correct."
+ exit 2
+fi
+
+FILE_BASE=$(basename ${GRAPHDEF_FILE})
+MODEL_NAME="${FILE_BASE%.*}"
+TMPDIR=$(mktemp -d)
+trap "{ rm -rf $TMPDIR; }" EXIT
+
+"${ROOT}/bin/tf2circle" "${INFO_FILE}" "${GRAPHDEF_FILE}" "${TMPDIR}/${MODEL_NAME}.circle"
+"${ROOT}/bin/model2nnpkg.sh" -o "${OUTPUT_DIR}" "${TMPDIR}/${MODEL_NAME}.circle"
diff --git a/infra/packaging/res/tflite_schema.fbs b/infra/packaging/res/tflite_schema.fbs
new file mode 100644
index 000000000..3da3188c3
--- /dev/null
+++ b/infra/packaging/res/tflite_schema.fbs
@@ -0,0 +1,698 @@
+// Copyright 2017 The TensorFlow Authors. 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.
+
+// Revision History
+// Version 0: Initial version.
+// Version 1: Add subgraphs to schema.
+// Version 2: Rename operators to conform to NN API.
+// Version 3: Move buffer data from Model.Subgraph.Tensors to Model.Buffers.
+
+namespace tflite;
+
+// This corresponds to the version.
+file_identifier "TFL3";
+// File extension of any written files.
+file_extension "tflite";
+
+// The type of data stored in a tensor.
+enum TensorType : byte {
+ FLOAT32 = 0,
+ FLOAT16 = 1,
+ INT32 = 2,
+ UINT8 = 3,
+ INT64 = 4,
+ STRING = 5,
+ BOOL = 6,
+ INT16 = 7,
+ COMPLEX64 = 8,
+}
+
+// Parameters for converting a quantized tensor back to float. Given a
+// quantized value q, the corresponding float value f should be:
+// f = scale * (q - zero_point)
+table QuantizationParameters {
+ min:[float]; // For importing back into tensorflow.
+ max:[float]; // For importing back into tensorflow.
+ scale:[float]; // For dequantizing the tensor's values.
+ zero_point:[long];
+}
+
+table Tensor {
+ // The tensor shape. The meaning of each entry is operator-specific but
+ // builtin ops use: [batch size, height, width, number of channels] (That's
+ // Tensorflow's NHWC).
+ shape:[int];
+ type:TensorType;
+ // An index that refers to the buffers table at the root of the model. Or,
+ // if there is no data buffer associated (i.e. intermediate results), then
+ // this is 0 (which refers to an always existent empty buffer).
+ //
+ // The data_buffer itself is an opaque container, with the assumption that the
+ // target device is little-endian. In addition, all builtin operators assume
+ // the memory is ordered such that if `shape` is [4, 3, 2], then index
+ // [i, j, k] maps to data_buffer[i*3*2 + j*2 + k].
+ buffer:uint;
+ name:string; // For debugging and importing back into tensorflow.
+ quantization:QuantizationParameters; // Optional.
+
+ is_variable:bool = false;
+}
+
+// A list of builtin operators. Builtin operators are slightly faster than custom
+// ones, but not by much. Moreover, while custom operators accept an opaque
+// object containing configuration parameters, builtins have a predetermined
+// set of acceptable options.
+enum BuiltinOperator : byte {
+ ADD = 0,
+ AVERAGE_POOL_2D = 1,
+ CONCATENATION = 2,
+ CONV_2D = 3,
+ DEPTHWISE_CONV_2D = 4,
+ // DEPTH_TO_SPACE = 5,
+ DEQUANTIZE = 6,
+ EMBEDDING_LOOKUP = 7,
+ FLOOR = 8,
+ FULLY_CONNECTED = 9,
+ HASHTABLE_LOOKUP = 10,
+ L2_NORMALIZATION = 11,
+ L2_POOL_2D = 12,
+ LOCAL_RESPONSE_NORMALIZATION = 13,
+ LOGISTIC = 14,
+ LSH_PROJECTION = 15,
+ LSTM = 16,
+ MAX_POOL_2D = 17,
+ MUL = 18,
+ RELU = 19,
+ // NOTE(aselle): RELU_N1_TO_1 used to be called RELU1, but it was renamed
+ // since different model developers use RELU1 in different ways. Never
+ // create another op called RELU1.
+ RELU_N1_TO_1 = 20,
+ RELU6 = 21,
+ RESHAPE = 22,
+ RESIZE_BILINEAR = 23,
+ RNN = 24,
+ SOFTMAX = 25,
+ SPACE_TO_DEPTH = 26,
+ SVDF = 27,
+ TANH = 28,
+ // TODO(aselle): Consider rename to CONCATENATE_EMBEDDINGS
+ CONCAT_EMBEDDINGS = 29,
+ SKIP_GRAM = 30,
+ CALL = 31,
+ CUSTOM = 32,
+ EMBEDDING_LOOKUP_SPARSE = 33,
+ PAD = 34,
+ UNIDIRECTIONAL_SEQUENCE_RNN = 35,
+ GATHER = 36,
+ BATCH_TO_SPACE_ND = 37,
+ SPACE_TO_BATCH_ND = 38,
+ TRANSPOSE = 39,
+ MEAN = 40,
+ SUB = 41,
+ DIV = 42,
+ SQUEEZE = 43,
+ UNIDIRECTIONAL_SEQUENCE_LSTM = 44,
+ STRIDED_SLICE = 45,
+ BIDIRECTIONAL_SEQUENCE_RNN = 46,
+ EXP = 47,
+ TOPK_V2 = 48,
+ SPLIT = 49,
+ LOG_SOFTMAX = 50,
+ // DELEGATE is a special op type for the operations which are delegated to
+ // other backends.
+ // WARNING: Experimental interface, subject to change
+ DELEGATE = 51,
+ BIDIRECTIONAL_SEQUENCE_LSTM = 52,
+ CAST = 53,
+ PRELU = 54,
+ MAXIMUM = 55,
+ ARG_MAX = 56,
+ MINIMUM = 57,
+ LESS = 58,
+ NEG = 59,
+ PADV2 = 60,
+ GREATER = 61,
+ GREATER_EQUAL = 62,
+ LESS_EQUAL = 63,
+ SELECT = 64,
+ SLICE = 65,
+ SIN = 66,
+ TRANSPOSE_CONV = 67,
+ SPARSE_TO_DENSE = 68,
+ TILE = 69,
+ EXPAND_DIMS = 70,
+ EQUAL = 71,
+ NOT_EQUAL = 72,
+ LOG = 73,
+ SUM = 74,
+ SQRT = 75,
+ RSQRT = 76,
+ SHAPE = 77,
+ POW = 78,
+ ARG_MIN = 79,
+ FAKE_QUANT = 80,
+ REDUCE_PROD = 81,
+ REDUCE_MAX = 82,
+ PACK = 83,
+ LOGICAL_OR = 84,
+ ONE_HOT = 85,
+ LOGICAL_AND = 86,
+ LOGICAL_NOT = 87,
+ UNPACK = 88,
+ REDUCE_MIN = 89,
+ FLOOR_DIV = 90,
+ REDUCE_ANY = 91,
+ SQUARE = 92,
+ ZEROS_LIKE = 93,
+ FILL = 94,
+}
+
+// Options for the builtin operators.
+union BuiltinOptions {
+ Conv2DOptions,
+ DepthwiseConv2DOptions,
+ ConcatEmbeddingsOptions,
+ LSHProjectionOptions,
+ Pool2DOptions,
+ SVDFOptions,
+ RNNOptions,
+ FullyConnectedOptions,
+ SoftmaxOptions,
+ ConcatenationOptions,
+ AddOptions,
+ L2NormOptions,
+ LocalResponseNormalizationOptions,
+ LSTMOptions,
+ ResizeBilinearOptions,
+ CallOptions,
+ ReshapeOptions,
+ SkipGramOptions,
+ SpaceToDepthOptions,
+ EmbeddingLookupSparseOptions,
+ MulOptions,
+ PadOptions,
+ GatherOptions,
+ BatchToSpaceNDOptions,
+ SpaceToBatchNDOptions,
+ TransposeOptions,
+ ReducerOptions,
+ SubOptions,
+ DivOptions,
+ SqueezeOptions,
+ SequenceRNNOptions,
+ StridedSliceOptions,
+ ExpOptions,
+ TopKV2Options,
+ SplitOptions,
+ LogSoftmaxOptions,
+ CastOptions,
+ DequantizeOptions,
+ MaximumMinimumOptions,
+ ArgMaxOptions,
+ LessOptions,
+ NegOptions,
+ PadV2Options,
+ GreaterOptions,
+ GreaterEqualOptions,
+ LessEqualOptions,
+ SelectOptions,
+ SliceOptions,
+ TransposeConvOptions,
+ SparseToDenseOptions,
+ TileOptions,
+ ExpandDimsOptions,
+ EqualOptions,
+ NotEqualOptions,
+ ShapeOptions,
+ PowOptions,
+ ArgMinOptions,
+ FakeQuantOptions,
+ PackOptions,
+ LogicalOrOptions,
+ OneHotOptions,
+ LogicalAndOptions,
+ LogicalNotOptions,
+ UnpackOptions,
+ FloorDivOptions,
+ SquareOptions,
+ ZerosLikeOptions,
+ FillOptions,
+}
+
+enum Padding : byte { SAME, VALID }
+
+enum ActivationFunctionType : byte {
+ NONE = 0,
+ RELU = 1,
+ RELU_N1_TO_1 = 2,
+ RELU6 = 3,
+ TANH = 4,
+ SIGN_BIT = 5,
+}
+
+table Conv2DOptions {
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+ fused_activation_function:ActivationFunctionType;
+ dilation_w_factor:int = 1;
+ dilation_h_factor:int = 1;
+}
+
+table Pool2DOptions {
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+ filter_width:int;
+ filter_height:int;
+ fused_activation_function:ActivationFunctionType;
+}
+
+table DepthwiseConv2DOptions {
+ // Parameters for DepthwiseConv version 1 or above.
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+ depth_multiplier:int;
+ fused_activation_function:ActivationFunctionType;
+ // Parameters for DepthwiseConv version 2 or above.
+ dilation_w_factor:int = 1;
+ dilation_h_factor:int = 1;
+}
+
+table ConcatEmbeddingsOptions {
+ num_channels:int;
+ num_columns_per_channel:[int];
+ embedding_dim_per_channel:[int]; // This could be inferred from parameters.
+}
+
+enum LSHProjectionType: byte {
+ UNKNOWN = 0,
+ SPARSE = 1,
+ DENSE = 2,
+}
+
+table LSHProjectionOptions {
+ type: LSHProjectionType;
+}
+
+table SVDFOptions {
+ rank:int;
+ fused_activation_function:ActivationFunctionType;
+}
+
+// An implementation of TensorFlow RNNCell.
+table RNNOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+// An implementation of TensorFlow dynamic_rnn with RNNCell.
+table SequenceRNNOptions {
+ time_major:bool;
+ fused_activation_function:ActivationFunctionType;
+}
+
+// An implementation of TensorFlow bidrectional_dynamic_rnn with RNNCell.
+table BidirectionalSequenceRNNOptions {
+ time_major:bool;
+ fused_activation_function:ActivationFunctionType;
+}
+
+enum FullyConnectedOptionsWeightsFormat: byte {
+ DEFAULT = 0,
+ SHUFFLED4x16INT8 = 1,
+}
+
+// An implementation of TensorFlow fully_connected (a.k.a Dense) layer.
+table FullyConnectedOptions {
+ // Parameters for FullyConnected version 1 or above.
+ fused_activation_function:ActivationFunctionType;
+
+ // Parameters for FullyConnected version 2 or above.
+ weights_format:FullyConnectedOptionsWeightsFormat = DEFAULT;
+}
+
+table SoftmaxOptions {
+ beta: float;
+}
+
+// An implementation of TensorFlow concat.
+table ConcatenationOptions {
+ axis:int;
+ fused_activation_function:ActivationFunctionType;
+}
+
+table AddOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table MulOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table L2NormOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table LocalResponseNormalizationOptions {
+ radius:int;
+ bias:float;
+ alpha:float;
+ beta:float;
+}
+
+enum LSTMKernelType : byte {
+ // Full LSTM kernel which supports peephole and projection.
+ FULL = 0,
+ // Basic LSTM kernels. Equivalent to TensorFlow BasicLSTMCell.
+ BASIC = 1,
+}
+
+// An implementation of TensorFlow LSTMCell and CoupledInputForgetGateLSTMCell
+table LSTMOptions {
+ // Parameters for LSTM version 1 or above.
+ fused_activation_function:ActivationFunctionType;
+ cell_clip: float; // Optional, 0.0 means no clipping
+ proj_clip: float; // Optional, 0.0 means no clipping
+
+ // Parameters for LSTM version 2 or above.
+ // Basic kernel is only supported in version 2 or above.
+ kernel_type: LSTMKernelType = FULL;
+}
+
+table ResizeBilinearOptions {
+ new_height: int (deprecated);
+ new_width: int (deprecated);
+ align_corners: bool;
+}
+
+// A call operation options
+table CallOptions {
+ // The subgraph index that needs to be called.
+ subgraph:uint;
+}
+
+table PadOptions {
+}
+
+table PadV2Options {
+}
+
+table ReshapeOptions {
+ new_shape:[int];
+}
+
+table SpaceToBatchNDOptions {
+}
+
+table BatchToSpaceNDOptions {
+}
+
+table SkipGramOptions {
+ ngram_size: int;
+ max_skip_size: int;
+ include_all_ngrams: bool;
+}
+
+table SpaceToDepthOptions {
+ block_size: int;
+}
+
+table SubOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table DivOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table TopKV2Options {
+}
+
+enum CombinerType : byte {
+ SUM = 0,
+ MEAN = 1,
+ SQRTN = 2,
+}
+
+table EmbeddingLookupSparseOptions {
+ combiner:CombinerType;
+}
+
+table GatherOptions {
+ axis: int;
+}
+
+table TransposeOptions {
+}
+
+table ExpOptions {
+}
+
+table ReducerOptions {
+ keep_dims: bool;
+}
+
+table SqueezeOptions {
+ squeeze_dims:[int];
+}
+
+table SplitOptions {
+ num_splits: int;
+}
+
+table StridedSliceOptions {
+ begin_mask: int;
+ end_mask: int;
+ ellipsis_mask: int;
+ new_axis_mask: int;
+ shrink_axis_mask: int;
+}
+
+table LogSoftmaxOptions {
+}
+
+table CastOptions {
+ in_data_type: TensorType;
+ out_data_type: TensorType;
+}
+
+table DequantizeOptions {
+}
+
+table MaximumMinimumOptions {
+}
+
+table TileOptions {
+}
+
+table ArgMaxOptions {
+ output_type : TensorType;
+}
+
+table ArgMinOptions {
+ output_type : TensorType;
+}
+
+table GreaterOptions {
+}
+
+table GreaterEqualOptions {
+}
+
+table LessOptions {
+}
+
+table LessEqualOptions {
+}
+
+table NegOptions {
+}
+
+table SelectOptions {
+}
+
+table SliceOptions {
+}
+
+table TransposeConvOptions {
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+}
+
+table ExpandDimsOptions {
+}
+
+table SparseToDenseOptions {
+ validate_indices:bool;
+}
+
+table EqualOptions {
+}
+
+table NotEqualOptions {
+}
+
+table ShapeOptions {
+ // Optional output type of the operation (int32 or int64). Defaults to int32.
+ out_type : TensorType;
+}
+
+table PowOptions {
+}
+
+table FakeQuantOptions {
+ // Parameters supported by version 1:
+ min:float;
+ max:float;
+ num_bits:int;
+
+ // Parameters supported by version 2:
+ narrow_range:bool;
+}
+
+table PackOptions {
+ values_count:int;
+ axis:int;
+}
+
+table LogicalOrOptions {
+}
+
+table OneHotOptions {
+ axis:int;
+}
+
+table LogicalAndOptions {
+}
+
+table LogicalNotOptions {
+}
+
+table UnpackOptions {
+ num:int;
+ axis:int;
+}
+
+table FloorDivOptions {
+}
+
+table SquareOptions {
+}
+
+table ZerosLikeOptions {
+}
+
+table FillOptions {
+}
+
+// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a
+// builtin, or a string if the operator is custom.
+table OperatorCode {
+ builtin_code:BuiltinOperator;
+ custom_code:string;
+
+ // The version of the operator. The version need to be bumped whenever new
+ // parameters are introduced into an op.
+ version:int = 1;
+}
+
+enum CustomOptionsFormat : byte {
+ FLEXBUFFERS = 0,
+}
+
+// An operator takes tensors as inputs and outputs. The type of operation being
+// performed is determined by an index into the list of valid OperatorCodes,
+// while the specifics of each operations is configured using builtin_options
+// or custom_options.
+table Operator {
+ // Index into the operator_codes array. Using an integer here avoids
+ // complicate map lookups.
+ opcode_index:uint;
+
+ // Optional input and output tensors are indicated by -1.
+ inputs:[int];
+ outputs:[int];
+
+ builtin_options:BuiltinOptions;
+ custom_options:[ubyte];
+ custom_options_format:CustomOptionsFormat;
+
+ // A list of booleans indicating the input tensors which are being mutated by
+ // this operator.(e.g. used by RNN and LSTM).
+ // For example, if the "inputs" array refers to 5 tensors and the second and
+ // fifth are mutable variables, then this list will contain
+ // [false, true, false, false, true].
+ //
+ // If the list is empty, no variable is mutated in this operator.
+ // The list either has the same length as `inputs`, or is empty.
+ mutating_variable_inputs:[bool];
+}
+
+// The root type, defining a subgraph, which typically represents an entire
+// model.
+table SubGraph {
+ // A list of all tensors used in this subgraph.
+ tensors:[Tensor];
+
+ // Indices of the tensors that are inputs into this subgraph. Note this is
+ // the list of non-static tensors that feed into the subgraph for inference.
+ inputs:[int];
+
+ // Indices of the tensors that are outputs out of this subgraph. Note this is
+ // the list of output tensors that are considered the product of the
+ // subgraph's inference.
+ outputs:[int];
+
+ // All operators, in execution order.
+ operators:[Operator];
+
+ // Name of this subgraph (used for debugging).
+ name:string;
+}
+
+// Table of raw data buffers (used for constant tensors). Referenced by tensors
+// by index. The generous alignment accommodates mmap-friendly data structures.
+table Buffer {
+ data:[ubyte] (force_align: 16);
+}
+
+table Model {
+ // Version of the schema.
+ version:uint;
+
+ // A list of all operator codes used in this model. This is
+ // kept in order because operators carry an index into this
+ // vector.
+ operator_codes:[OperatorCode];
+
+ // All the subgraphs of the model. The 0th is assumed to be the main
+ // model.
+ subgraphs:[SubGraph];
+
+ // A description of the model.
+ description:string;
+
+ // Buffers of the model.
+ // Note the 0th entry of this array must be an empty buffer (sentinel).
+ // This is a convention so that tensors without a buffer can provide 0 as
+ // their buffer.
+ buffers:[Buffer];
+
+ // Metadata about the model. Indirects into the existings buffers list.
+ metadata_buffer:[int];
+}
+
+root_type Model;
diff --git a/infra/packaging/verify b/infra/packaging/verify
new file mode 100644
index 000000000..4d5e610af
--- /dev/null
+++ b/infra/packaging/verify
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+#
+# HOW TO USE
+#
+# ./nnas verify-package [CHECK 1] [CHECK 2] ... [CHECK N]
+#
+# REQUIRE: N >= 1
+#
+SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+if [[ -z "${NNAS_PROJECT_PATH}" ]]; then
+ echo "ERROR: NNAS_PROJECT_PATH is not specified"
+ exit 255
+fi
+
+CHECKLIST=()
+
+while [ "$#" -ne 0 ]; do
+ CUR="$1"
+
+ case $CUR in
+ '--prefix')
+ NNAS_INSTALL_PREFIX="$2"
+ shift 2
+ ;;
+ *)
+ if [[ ! -f "${SCRIPT_PATH}/chklist/$CUR" ]]; then
+ echo "ERROR: '${CUR}' is invalid"
+ # TODO Show supported checks
+ exit 255
+ fi
+
+ CHECKLIST+=("${CUR}")
+ shift 1
+ ;;
+ esac
+done
+
+# Q. Is it better to have the default value for NNAS_INSTALL_PREFIX?
+# TODO Show USAGE
+# TODO Use a proper exitcode on error (http://tldp.org/LDP/abs/html/exitcodes.html)
+if [[ -z "${NNAS_INSTALL_PREFIX}" ]]; then
+ echo "ERROR: --prefix is not specified"
+ exit 255
+fi
+
+if [[ ${#CHECKLIST[@]} -eq 0 ]]; then
+ echo "ERROR: Check is not specified"
+ exit 255
+fi
+
+EXITCODE=0
+
+for CHECK_NAME in ${CHECKLIST[@]}; do
+ source "${SCRIPT_PATH}/chklist/${CHECK_NAME}"
+
+ prepare
+
+ echo -n "${QUESTION}"
+
+ PASSED=0
+
+ run
+
+ if [[ ${PASSED} -ne 0 ]]; then
+ ANSWER="Yes"
+ else
+ ANSWER="No"
+ # Reference: https://www.tldp.org/LDP/abs/html/exitcodes.html
+ EXITCODE=1
+ fi
+
+ echo " - ${ANSWER}"
+done
+
+if [[ ${EXITCODE} -ne 0 ]]; then
+ echo
+ echo "FAIL"
+fi
+
+exit ${EXITCODE}