From f974ff97e8af33ae43f9705fd05c324356e3bce5 Mon Sep 17 00:00:00 2001 From: Mehrdad Hessar Date: Wed, 27 Jul 2022 15:12:12 -0700 Subject: [PATCH 1/6] move cmake args to generate project --- .../template_project/CMakeLists.txt.template | 2 +- .../template_project/microtvm_api_server.py | 55 ++++++++++--------- .../how_to/work_with_microtvm/micro_tvmc.sh | 10 ++-- tests/micro/common/test_tvmc.py | 8 +-- 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/apps/microtvm/zephyr/template_project/CMakeLists.txt.template b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template index 710cf3550c0d..7f37efc599c2 100644 --- a/apps/microtvm/zephyr/template_project/CMakeLists.txt.template +++ b/apps/microtvm/zephyr/template_project/CMakeLists.txt.template @@ -23,7 +23,7 @@ set(ENV{QEMU_BIN_PATH} "${CMAKE_SOURCE_DIR}/qemu-hack") set(QEMU_PIPE "\${QEMU_PIPE}") # QEMU_PIPE is set by the calling TVM instance. -set(ENABLE_CMSIS ) + find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) project(microtvm_autogenerated_project) diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py index 7b9538f6ce03..ca47c6455d47 100644 --- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -275,13 +275,13 @@ def _get_nrf_device_args(options): ), server.ProjectOption( "verbose", - optional=["build"], + optional=["generate_project"], type="bool", help="Run build with verbose output.", ), server.ProjectOption( "west_cmd", - optional=["build"], + optional=["generate_project"], default=WEST_CMD, type="str", help=( @@ -292,14 +292,14 @@ def _get_nrf_device_args(options): server.ProjectOption( "zephyr_base", required=(["generate_project", "open_transport"] if not ZEPHYR_BASE else None), - optional=(["generate_project", "open_transport", "build"] if ZEPHYR_BASE else ["build"]), + optional=(["generate_project", "open_transport"] if ZEPHYR_BASE else ["build"]), default=ZEPHYR_BASE, type="str", help="Path to the zephyr base directory.", ), server.ProjectOption( "zephyr_board", - required=["generate_project", "build", "flash", "open_transport"], + required=["generate_project", "flash", "open_transport"], choices=list(BOARD_PROPERTIES), type="str", help="Name of the Zephyr board to build for.", @@ -419,7 +419,7 @@ def _create_prj_conf(self, project_dir, options): f.write("\n") API_SERVER_CRT_LIBS_TOKEN = "" - ENABLE_CMSIS_TOKEN = "" + CMAKE_ARGS = "" CRT_LIBS_BY_PROJECT_TYPE = { "host_driven": "microtvm_rpc_server microtvm_rpc_common aot_executor_module aot_executor common", @@ -457,6 +457,28 @@ def _cmsis_required(self, project_path: Union[str, pathlib.Path]) -> bool: return True return False + def _generate_cmake_args(self, mlf_extracted_path, options) -> str: + cmake_args = "\n# cmake args\n" + if options.get("verbose"): + cmake_args += "set(CMAKE_VERBOSE_MAKEFILE TRUE)\n" + + if options.get("zephyr_base"): + cmake_args += f"set(ZEPHYR_BASE {options['zephyr_base']})\n" + + if options.get("west_cmd"): + cmake_args += f"set(WEST {options['west_cmd']})\n" + + if self._is_qemu(options): + # Some boards support more than one emulator, so ensure QEMU is set. + cmake_args += f"set(EMU_PLATFORM qemu)\n" + + cmake_args += f"set(BOARD {options['zephyr_board']})\n" + + enable_cmsis = self._cmsis_required(mlf_extracted_path) + cmake_args += f"set(ENABLE_CMSIS {str(enable_cmsis).upper()})\n" + + return cmake_args + def generate_project(self, model_library_format_path, standalone_crt_dir, project_dir, options): # Check Zephyr version version = self._get_platform_version(get_zephyr_base(options)) @@ -510,9 +532,8 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec crt_libs = self.CRT_LIBS_BY_PROJECT_TYPE[options["project_type"]] line = line.replace("", crt_libs) - if self.ENABLE_CMSIS_TOKEN in line: - enable_cmsis = self._cmsis_required(extract_path) - line = line.replace(self.ENABLE_CMSIS_TOKEN, str(enable_cmsis).upper()) + if self.CMAKE_ARGS in line: + line = self._generate_cmake_args(extract_path, options) cmake_f.write(line) @@ -542,23 +563,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec def build(self, options): BUILD_DIR.mkdir() - cmake_args = ["cmake", ".."] - if options.get("verbose"): - cmake_args.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE") - - if options.get("zephyr_base"): - cmake_args.append(f"-DZEPHYR_BASE:STRING={options['zephyr_base']}") - - if options.get("west_cmd"): - cmake_args.append(f"-DWEST={options['west_cmd']}") - - if self._is_qemu(options): - # Some boards support more than one emulator, so ensure QEMU is set. - cmake_args.append(f"-DEMU_PLATFORM=qemu") - - cmake_args.append(f"-DBOARD:STRING={options['zephyr_board']}") - - check_call(cmake_args, cwd=BUILD_DIR) + check_call(["cmake", ".."], cwd=BUILD_DIR) args = ["make", "-j2"] if options.get("verbose"): diff --git a/gallery/how_to/work_with_microtvm/micro_tvmc.sh b/gallery/how_to/work_with_microtvm/micro_tvmc.sh index 0b789216f21b..92f138026e30 100755 --- a/gallery/how_to/work_with_microtvm/micro_tvmc.sh +++ b/gallery/how_to/work_with_microtvm/micro_tvmc.sh @@ -121,7 +121,9 @@ tvmc compile magic_wand.tflite \ # # To generate a Zephyr project we use TVM Micro subcommand ``create``. We pass the MLF format and the path # for the project to ``create`` subcommand along with project options. Project options for each -# platform (Zephyr/Arduino) are defined in their Project API server file. To generate Zephyr project, run: +# platform (Zephyr/Arduino) are defined in their Project API server file. To build +# Zephyr project for a different Zephyr board, change ``zephyr_board`` project option. +# To generate Zephyr project, run: # # bash tvmc micro create \ @@ -151,11 +153,9 @@ tvmc micro create \ # bash tvmc micro build \ project \ - zephyr \ - --project-option zephyr_board=qemu_x86 + zephyr # bash -# This will build the project in ``project`` directory and generates binary files under ``project/build``. To build -# Zephyr project for a different Zephyr board, change ``zephyr_board`` project option. +# This will build the project in ``project`` directory and generates binary files under ``project/build``. # # Next, we flash the Zephyr binary file to Zephyr device. For ``qemu_x86`` Zephyr board this step does not # actually perform any action since QEMU will be used, however you need this step for physical hardware. diff --git a/tests/micro/common/test_tvmc.py b/tests/micro/common/test_tvmc.py index 096e12393d43..2ca2a68c3adc 100644 --- a/tests/micro/common/test_tvmc.py +++ b/tests/micro/common/test_tvmc.py @@ -108,9 +108,7 @@ def test_tvmc_model_build_only(platform, board, output_dir): cmd_result = _run_tvmc(create_project_cmd) assert cmd_result == 0, "tvmc micro failed in step: create-project" - cmd_result = _run_tvmc( - ["micro", "build", project_dir, platform, "--project-option", f"{platform}_board={board}"] - ) + cmd_result = _run_tvmc(["micro", "build", project_dir, platform]) assert cmd_result == 0, "tvmc micro failed in step: build" shutil.rmtree(output_dir) @@ -174,9 +172,7 @@ def test_tvmc_model_run(platform, board, output_dir): cmd_result = _run_tvmc(create_project_cmd) assert cmd_result == 0, "tvmc micro failed in step: create-project" - cmd_result = _run_tvmc( - ["micro", "build", project_dir, platform, "--project-option", f"{platform}_board={board}"] - ) + cmd_result = _run_tvmc(["micro", "build", project_dir, platform]) assert cmd_result == 0, "tvmc micro failed in step: build" cmd_result = _run_tvmc( From 9e46b60b62bd73b3ea7fa78a896edf6911632e02 Mon Sep 17 00:00:00 2001 From: Mehrdad Hessar Date: Thu, 28 Jul 2022 11:33:08 -0700 Subject: [PATCH 2/6] remove zephyr board from flash and run --- .../template_project/microtvm_api_server.py | 43 +++++++++++++------ .../how_to/work_with_microtvm/micro_tvmc.sh | 4 +- tests/micro/common/test_tvmc.py | 6 +-- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py index ca47c6455d47..f8734f65e4de 100644 --- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -61,6 +61,8 @@ BOARDS = API_SERVER_DIR / "boards.json" +CMAKELIST_FILENAME = "CMakeLists.txt" + # Used to check Zephyr version installed on the host. # We only check two levels of the version. ZEPHYR_VERSION = 2.7 @@ -299,7 +301,7 @@ def _get_nrf_device_args(options): ), server.ProjectOption( "zephyr_board", - required=["generate_project", "flash", "open_transport"], + required=["generate_project"], choices=list(BOARD_PROPERTIES), type="str", help="Name of the Zephyr board to build for.", @@ -468,7 +470,7 @@ def _generate_cmake_args(self, mlf_extracted_path, options) -> str: if options.get("west_cmd"): cmake_args += f"set(WEST {options['west_cmd']})\n" - if self._is_qemu(options): + if self._is_qemu(options["zephyr_board"]): # Some boards support more than one emulator, so ensure QEMU is set. cmake_args += f"set(EMU_PLATFORM qemu)\n" @@ -510,7 +512,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec os.makedirs(extract_path) tf.extractall(path=extract_path) - if self._is_qemu(options): + if self._is_qemu(options["zephyr_board"]): shutil.copytree(API_SERVER_DIR / "qemu-hack", project_dir / "qemu-hack") # Populate CRT. @@ -525,8 +527,8 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec shutil.copy2(src_path, dst_path) # Populate Makefile. - with open(project_dir / "CMakeLists.txt", "w") as cmake_f: - with open(API_SERVER_DIR / "CMakeLists.txt.template", "r") as cmake_template_f: + with open(project_dir / CMAKELIST_FILENAME, "w") as cmake_f: + with open(API_SERVER_DIR / f"{CMAKELIST_FILENAME}.template", "r") as cmake_template_f: for line in cmake_template_f: if self.API_SERVER_CRT_LIBS_TOKEN in line: crt_libs = self.CRT_LIBS_BY_PROJECT_TYPE[options["project_type"]] @@ -576,22 +578,33 @@ def build(self, options): _KNOWN_QEMU_ZEPHYR_BOARDS = ("mps2_an521", "mps3_an547") @classmethod - def _is_qemu(cls, options): - return ( - "qemu" in options["zephyr_board"] - or options["zephyr_board"] in cls._KNOWN_QEMU_ZEPHYR_BOARDS - ) + def _is_qemu(cls, board: str) -> bool: + return "qemu" in board or board in cls._KNOWN_QEMU_ZEPHYR_BOARDS @classmethod def _has_fpu(cls, zephyr_board): fpu_boards = [name for name, board in BOARD_PROPERTIES.items() if board["fpu"]] return zephyr_board in fpu_boards + @classmethod + def _find_board_from_cmake_file(cls) -> str: + with open(API_SERVER_DIR / CMAKELIST_FILENAME) as cmake_f: + for line in cmake_f: + if line.startswith("set(BOARD"): + zephyr_board = line.strip("\n").strip("set(BOARD ").strip(")") + break + + if not zephyr_board: + raise RuntimeError( + f"Zephyr Board is not found in the {API_SERVER_DIR / CMAKELIST_FILENAME}" + ) + return zephyr_board + def flash(self, options): - if self._is_qemu(options): - return # NOTE: qemu requires no flash step--it is launched from open_transport. + zephyr_board = self._find_board_from_cmake_file() - zephyr_board = options["zephyr_board"] + if self._is_qemu(zephyr_board): + return # NOTE: qemu requires no flash step--it is launched from open_transport. # The nRF5340DK requires an additional `nrfjprog --recover` before each flash cycle. # This is because readback protection is enabled by default when this device is flashed. @@ -606,7 +619,9 @@ def flash(self, options): check_call(["make", "flash"], cwd=API_SERVER_DIR / "build") def open_transport(self, options): - if self._is_qemu(options): + zephyr_board = self._find_board_from_cmake_file() + + if self._is_qemu(zephyr_board): transport = ZephyrQemuTransport(options) else: transport = ZephyrSerialTransport(options) diff --git a/gallery/how_to/work_with_microtvm/micro_tvmc.sh b/gallery/how_to/work_with_microtvm/micro_tvmc.sh index 92f138026e30..c35314b9ebc1 100755 --- a/gallery/how_to/work_with_microtvm/micro_tvmc.sh +++ b/gallery/how_to/work_with_microtvm/micro_tvmc.sh @@ -163,8 +163,7 @@ tvmc micro build \ # bash tvmc micro flash \ project \ - zephyr \ - --project-option zephyr_board=qemu_x86 + zephyr # bash ############################################################ @@ -181,7 +180,6 @@ tvmc micro flash \ tvmc run \ --device micro \ project \ - --project-option zephyr_board=qemu_x86 \ --fill-mode ones \ --print-top 4 # bash diff --git a/tests/micro/common/test_tvmc.py b/tests/micro/common/test_tvmc.py index 2ca2a68c3adc..2043d91108fa 100644 --- a/tests/micro/common/test_tvmc.py +++ b/tests/micro/common/test_tvmc.py @@ -175,9 +175,7 @@ def test_tvmc_model_run(platform, board, output_dir): cmd_result = _run_tvmc(["micro", "build", project_dir, platform]) assert cmd_result == 0, "tvmc micro failed in step: build" - cmd_result = _run_tvmc( - ["micro", "flash", project_dir, platform, "--project-option", f"{platform}_board={board}"] - ) + cmd_result = _run_tvmc(["micro", "flash", project_dir, platform]) assert cmd_result == 0, "tvmc micro failed in step: flash" cmd_result = _run_tvmc( @@ -186,8 +184,6 @@ def test_tvmc_model_run(platform, board, output_dir): "--device", "micro", project_dir, - "--project-option", - f"{platform}_board={board}", "--fill-mode", "random", ] From 7b4b54c6b1f9138e9170849368aef9088b97ec11 Mon Sep 17 00:00:00 2001 From: Mehrdad Hessar Date: Thu, 28 Jul 2022 15:07:10 -0700 Subject: [PATCH 3/6] address comments --- .../zephyr/template_project/microtvm_api_server.py | 7 ++++--- gallery/how_to/work_with_microtvm/micro_tvmc.sh | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py index f8734f65e4de..0c0f045b5fc1 100644 --- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -421,7 +421,7 @@ def _create_prj_conf(self, project_dir, options): f.write("\n") API_SERVER_CRT_LIBS_TOKEN = "" - CMAKE_ARGS = "" + CMAKE_ARGS_TOKEN = "" CRT_LIBS_BY_PROJECT_TYPE = { "host_driven": "microtvm_rpc_server microtvm_rpc_common aot_executor_module aot_executor common", @@ -534,7 +534,7 @@ def generate_project(self, model_library_format_path, standalone_crt_dir, projec crt_libs = self.CRT_LIBS_BY_PROJECT_TYPE[options["project_type"]] line = line.replace("", crt_libs) - if self.CMAKE_ARGS in line: + if self.CMAKE_ARGS_TOKEN in line: line = self._generate_cmake_args(extract_path, options) cmake_f.write(line) @@ -588,6 +588,7 @@ def _has_fpu(cls, zephyr_board): @classmethod def _find_board_from_cmake_file(cls) -> str: + zephyr_board = None with open(API_SERVER_DIR / CMAKELIST_FILENAME) as cmake_f: for line in cmake_f: if line.startswith("set(BOARD"): @@ -596,7 +597,7 @@ def _find_board_from_cmake_file(cls) -> str: if not zephyr_board: raise RuntimeError( - f"Zephyr Board is not found in the {API_SERVER_DIR / CMAKELIST_FILENAME}" + f"No Zephyr board set in the {API_SERVER_DIR / CMAKELIST_FILENAME}." ) return zephyr_board diff --git a/gallery/how_to/work_with_microtvm/micro_tvmc.sh b/gallery/how_to/work_with_microtvm/micro_tvmc.sh index c35314b9ebc1..5ec718884559 100755 --- a/gallery/how_to/work_with_microtvm/micro_tvmc.sh +++ b/gallery/how_to/work_with_microtvm/micro_tvmc.sh @@ -122,7 +122,7 @@ tvmc compile magic_wand.tflite \ # To generate a Zephyr project we use TVM Micro subcommand ``create``. We pass the MLF format and the path # for the project to ``create`` subcommand along with project options. Project options for each # platform (Zephyr/Arduino) are defined in their Project API server file. To build -# Zephyr project for a different Zephyr board, change ``zephyr_board`` project option. +# Zephyr project for a different Zephyr board, change ``zephyr_board`` project option. # To generate Zephyr project, run: # # bash From 7da0cb9db1b74ea35e6be6b214af2e11c87ac94d Mon Sep 17 00:00:00 2001 From: Mehrdad Hessar Date: Thu, 28 Jul 2022 15:24:47 -0700 Subject: [PATCH 4/6] fix bug --- .../template_project/microtvm_api_server.py | 4 +-- tests/micro/common/test_tvmc.py | 36 ++++++++++++++----- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/apps/microtvm/zephyr/template_project/microtvm_api_server.py b/apps/microtvm/zephyr/template_project/microtvm_api_server.py index 0c0f045b5fc1..291ad8cc425a 100644 --- a/apps/microtvm/zephyr/template_project/microtvm_api_server.py +++ b/apps/microtvm/zephyr/template_project/microtvm_api_server.py @@ -596,9 +596,7 @@ def _find_board_from_cmake_file(cls) -> str: break if not zephyr_board: - raise RuntimeError( - f"No Zephyr board set in the {API_SERVER_DIR / CMAKELIST_FILENAME}." - ) + raise RuntimeError(f"No Zephyr board set in the {API_SERVER_DIR / CMAKELIST_FILENAME}.") return zephyr_board def flash(self, options): diff --git a/tests/micro/common/test_tvmc.py b/tests/micro/common/test_tvmc.py index 2043d91108fa..82b793d6c808 100644 --- a/tests/micro/common/test_tvmc.py +++ b/tests/micro/common/test_tvmc.py @@ -53,7 +53,10 @@ def test_tvmc_exist(platform, board): @tvm.testing.requires_micro @pytest.mark.parametrize( "output_dir,", - [pathlib.Path("./tvmc_relative_path_test"), pathlib.Path(tempfile.mkdtemp())], + [ + pathlib.Path("./tvmc_relative_path_test"), + # pathlib.Path(tempfile.mkdtemp()) + ], ) def test_tvmc_model_build_only(platform, board, output_dir): target = tvm.micro.testing.get_target(platform, board) @@ -108,7 +111,10 @@ def test_tvmc_model_build_only(platform, board, output_dir): cmd_result = _run_tvmc(create_project_cmd) assert cmd_result == 0, "tvmc micro failed in step: create-project" - cmd_result = _run_tvmc(["micro", "build", project_dir, platform]) + build_cmd = ["micro", "build", project_dir, platform] + if platform == "arduino": + build_cmd += ["--project-option", f"{platform}_board={board}"] + cmd_result = _run_tvmc(build_cmd) assert cmd_result == 0, "tvmc micro failed in step: build" shutil.rmtree(output_dir) @@ -172,22 +178,34 @@ def test_tvmc_model_run(platform, board, output_dir): cmd_result = _run_tvmc(create_project_cmd) assert cmd_result == 0, "tvmc micro failed in step: create-project" - cmd_result = _run_tvmc(["micro", "build", project_dir, platform]) + build_cmd = ["micro", "build", project_dir, platform] + if platform == "arduino": + build_cmd += ["--project-option", f"{platform}_board={board}"] + cmd_result = _run_tvmc(build_cmd) + assert cmd_result == 0, "tvmc micro failed in step: build" - cmd_result = _run_tvmc(["micro", "flash", project_dir, platform]) + flash_cmd = ["micro", "flash", project_dir, platform] + if platform == "arduino": + flash_cmd += ["--project-option", f"{platform}_board={board}"] + cmd_result = _run_tvmc(flash_cmd) assert cmd_result == 0, "tvmc micro failed in step: flash" - cmd_result = _run_tvmc( + run_cmd = [ + "run", + "--device", + "micro", + project_dir, + ] + if platform == "arduino": + run_cmd += ["--project-option", f"{platform}_board={board}"] + run_cmd.append( [ - "run", - "--device", - "micro", - project_dir, "--fill-mode", "random", ] ) + cmd_result = _run_tvmc() assert cmd_result == 0, "tvmc micro failed in step: run" shutil.rmtree(output_dir) From 30515eba725e52e335736a2892913ffe11db5d19 Mon Sep 17 00:00:00 2001 From: Mehrdad Hessar Date: Thu, 28 Jul 2022 15:40:35 -0700 Subject: [PATCH 5/6] revert comment --- tests/micro/common/test_tvmc.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/micro/common/test_tvmc.py b/tests/micro/common/test_tvmc.py index 82b793d6c808..a24db9329a3d 100644 --- a/tests/micro/common/test_tvmc.py +++ b/tests/micro/common/test_tvmc.py @@ -53,10 +53,7 @@ def test_tvmc_exist(platform, board): @tvm.testing.requires_micro @pytest.mark.parametrize( "output_dir,", - [ - pathlib.Path("./tvmc_relative_path_test"), - # pathlib.Path(tempfile.mkdtemp()) - ], + [pathlib.Path("./tvmc_relative_path_test"), pathlib.Path(tempfile.mkdtemp())], ) def test_tvmc_model_build_only(platform, board, output_dir): target = tvm.micro.testing.get_target(platform, board) @@ -205,7 +202,7 @@ def test_tvmc_model_run(platform, board, output_dir): "random", ] ) - cmd_result = _run_tvmc() + cmd_result = _run_tvmc(run_cmd) assert cmd_result == 0, "tvmc micro failed in step: run" shutil.rmtree(output_dir) From 59827dde88adfa89604caacc45bbe7ede95bf163 Mon Sep 17 00:00:00 2001 From: Mehrdad Hessar Date: Thu, 28 Jul 2022 15:47:15 -0700 Subject: [PATCH 6/6] fix bug --- tests/micro/common/test_tvmc.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/micro/common/test_tvmc.py b/tests/micro/common/test_tvmc.py index a24db9329a3d..bd11b579e654 100644 --- a/tests/micro/common/test_tvmc.py +++ b/tests/micro/common/test_tvmc.py @@ -196,12 +196,7 @@ def test_tvmc_model_run(platform, board, output_dir): ] if platform == "arduino": run_cmd += ["--project-option", f"{platform}_board={board}"] - run_cmd.append( - [ - "--fill-mode", - "random", - ] - ) + run_cmd += ["--fill-mode", "random"] cmd_result = _run_tvmc(run_cmd) assert cmd_result == 0, "tvmc micro failed in step: run" shutil.rmtree(output_dir)