Repository: ARMmbed/mbed-tools

Issue:
Allow setting custom output directory in "configure" sub command
**Is your feature request related to a problem? Please describe.**
I use a custom build tool (that calls cmake under the hood) to build packages in my project. The fact mbed-tools use such an unusual build directory makes things awkward to integrate. 

**Describe the solution you'd like**
It would be great if mbed-tools could allow passing an "-o/--output-dir" option to configure that would allow us to customize where mbed_config.cmake will be written to.

**Describe alternatives you've considered**
Heuristically trying to find out where mbed_config.cmake was generated and moving to an appropriate location. This solution is suboptimal, at best.

Comments:

Patch:
diff --git a/news/286.bugfix b/news/286.bugfix
new file mode 100644
index 0000000..745bfb1
--- /dev/null
+++ b/news/286.bugfix
@@ -0,0 +1,1 @@
+Properly handle --custom-targets-json in configure sub command
diff --git a/news/287.bugfix b/news/287.bugfix
new file mode 100644
index 0000000..586d532
--- /dev/null
+++ b/news/287.bugfix
@@ -0,0 +1,1 @@
+Allow choosing output directory in configure sub command
diff --git a/src/mbed_tools/cli/configure.py b/src/mbed_tools/cli/configure.py
index 71e2c79..553266e 100644
--- a/src/mbed_tools/cli/configure.py
+++ b/src/mbed_tools/cli/configure.py
@@ -14,6 +14,9 @@ from mbed_tools.build import generate_config
 @click.command(
     help="Generate an Mbed OS config CMake file and write it to a .mbedbuild folder in the program directory."
 )
+@click.option(
+    "--custom-targets-json", type=click.Path(), default=None, help="Path to custom_targets.json.",
+)
 @click.option(
     "-t",
     "--toolchain",
@@ -22,6 +25,7 @@ from mbed_tools.build import generate_config
     help="The toolchain you are using to build your app.",
 )
 @click.option("-m", "--mbed-target", required=True, help="A build target for an Mbed-enabled device, eg. K64F")
+@click.option("-o", "--output-dir", type=click.Path(), default=None, help="Path to output directory.")
 @click.option(
     "-p",
     "--program-path",
@@ -32,7 +36,9 @@ from mbed_tools.build import generate_config
 @click.option(
     "--mbed-os-path", type=click.Path(), default=None, help="Path to local Mbed OS directory.",
 )
-def configure(toolchain: str, mbed_target: str, program_path: str, mbed_os_path: str) -> None:
+def configure(
+    toolchain: str, mbed_target: str, program_path: str, mbed_os_path: str, output_dir: str, custom_targets_json: str
+) -> None:
     """Exports a mbed_config.cmake file to build directory in the program root.
 
     The parameters set in the CMake file will be dependent on the combination of
@@ -43,16 +49,23 @@ def configure(toolchain: str, mbed_target: str, program_path: str, mbed_os_path:
     exist.
 
     Args:
+        custom_targets_json: the path to custom_targets.json
         toolchain: the toolchain you are using (eg. GCC_ARM, ARM)
         mbed_target: the target you are building for (eg. K64F)
         program_path: the path to the local Mbed program
         mbed_os_path: the path to the local Mbed OS directory
+        output_dir: the path to the output directory
     """
     cmake_build_subdir = pathlib.Path(mbed_target.upper(), "develop", toolchain.upper())
     if mbed_os_path is None:
         program = MbedProgram.from_existing(pathlib.Path(program_path), cmake_build_subdir)
     else:
         program = MbedProgram.from_existing(pathlib.Path(program_path), cmake_build_subdir, pathlib.Path(mbed_os_path))
+    if custom_targets_json is not None:
+        program.files.custom_targets_json = pathlib.Path(custom_targets_json)
+    if output_dir is not None:
+        program.files.cmake_build_dir = pathlib.Path(output_dir)
+
     mbed_target = mbed_target.upper()
     output_path = generate_config(mbed_target, toolchain, program)
     click.echo(f"mbed_config.cmake has been generated and written to '{str(output_path.resolve())}'")


Test patch:
diff --git a/tests/cli/test_configure.py b/tests/cli/test_configure.py
index edb2341..a0c61fd 100644
--- a/tests/cli/test_configure.py
+++ b/tests/cli/test_configure.py
@@ -2,6 +2,8 @@
 # Copyright (c) 2020-2021 Arm Limited and Contributors. All rights reserved.
 # SPDX-License-Identifier: Apache-2.0
 #
+import pathlib
+
 from unittest import TestCase, mock
 
 from click.testing import CliRunner
@@ -23,3 +25,25 @@ class TestConfigureCommand(TestCase):
         CliRunner().invoke(configure, ["-m", "k64f", "-t", "gcc_arm", "--mbed-os-path", "./extern/mbed-os"])
 
         generate_config.assert_called_once_with("K64F", "GCC_ARM", program.from_existing())
+
+    @mock.patch("mbed_tools.cli.configure.generate_config")
+    @mock.patch("mbed_tools.cli.configure.MbedProgram")
+    def test_custom_targets_location_used_when_passed(self, program, generate_config):
+        program = program.from_existing()
+        custom_targets_json_path = pathlib.Path("custom", "custom_targets.json")
+        CliRunner().invoke(
+            configure, ["-t", "gcc_arm", "-m", "k64f", "--custom-targets-json", custom_targets_json_path]
+        )
+
+        generate_config.assert_called_once_with("K64F", "GCC_ARM", program)
+        self.assertEqual(program.files.custom_targets_json, custom_targets_json_path)
+
+    @mock.patch("mbed_tools.cli.configure.generate_config")
+    @mock.patch("mbed_tools.cli.configure.MbedProgram")
+    def test_custom_output_directory_used_when_passed(self, program, generate_config):
+        program = program.from_existing()
+        output_dir = pathlib.Path("build")
+        CliRunner().invoke(configure, ["-t", "gcc_arm", "-m", "k64f", "-o", output_dir])
+
+        generate_config.assert_called_once_with("K64F", "GCC_ARM", program)
+        self.assertEqual(program.files.cmake_build_dir, output_dir)