[meson] Pull the functionality of make_build_bin.sh into Meson.

This change is part of the effort to implement the directory structure
described in #650.

Signed-off-by: Miguel Young de la Sota <mcyoung@google.com>
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index e86fbca..19d44d5 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -139,7 +139,6 @@
       ./meson_init.sh -f
       ninja -C "$(sw_obj_dir sim-verilator)" all
       ninja -C "$(sw_obj_dir fpga)" all
-      util/make_build_bin.sh
     displayName: 'Build embedded targets'
   - bash: |
       . util/build_consts.sh
diff --git a/meson.build b/meson.build
index 38b1483..c8dd7e1 100644
--- a/meson.build
+++ b/meson.build
@@ -20,6 +20,12 @@
   add_project_arguments('-DSIMULATION', language: 'c')
 endif
 
+dev_bin_dir = get_option('dev_bin_dir')
+host_bin_dir = get_option('host_bin_dir')
+if dev_bin_dir == 'undef' or host_bin_dir == 'undef'
+  error('dev_bin_dir option not set. Please run meson with a valid binary directory option.')
+endif
+
 # The following flags are applied to *all* builds, both cross builds
 # and native builds.
 add_project_arguments(
diff --git a/meson_init.sh b/meson_init.sh
index eaf494f..7f4102b 100755
--- a/meson_init.sh
+++ b/meson_init.sh
@@ -103,10 +103,14 @@
 
 for platform in ${PLATFORMS[@]}; do
   obj_dir="$(sw_obj_dir "$platform")"
+  bin_dir="$(sw_bin_dir "$platform")"
   mkdir -p "$obj_dir"
+  mkdir -p "$bin_dir"
   meson ${FLAGS_reconfigure} \
     -Dtarget="$platform" \
     -Dot_version="$OT_VERSION" \
+    -Ddev_bin_dir="$bin_dir" \
+    -Dhost_bin_dir="$HOST_BIN_DIR" \
     --cross-file="$CROSS_FILE" \
     --buildtype=plain \
     "$obj_dir"
diff --git a/meson_options.txt b/meson_options.txt
index 957be04..80f43d2 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,4 +1,25 @@
-option('target', type : 'combo', choices: ['sim-verilator', 'fpga', 'undef'], value : 'undef')
+option(
+  'target',
+  type: 'combo',
+  choices: [
+    'sim-verilator',
+    'fpga',
+    'undef',
+  ],
+  value: 'undef',
+)
+
+option(
+  'dev_bin_dir',
+  type: 'string',
+  value: 'undef',
+)
+
+option(
+  'host_bin_dir',
+  type: 'string',
+  value: 'undef',
+)
 
 option(
   'ot_version',
diff --git a/sw/device/boot_rom/meson.build b/sw/device/boot_rom/meson.build
index bcc3d63..fbf2045 100644
--- a/sw/device/boot_rom/meson.build
+++ b/sw/device/boot_rom/meson.build
@@ -21,30 +21,42 @@
 rom_link_args = ['-Wl,-T,@0@/@1@'.format(meson.source_root(), rom_linkfile[0])]
 rom_link_deps = [rom_linkfile]
 
-custom_target(
+boot_rom_elf = executable(
+  'boot_rom',
+  sources: [
+    'boot_rom.c',
+    'bootstrap.c',
+    'crt0.S'
+  ],
+  name_suffix: 'elf',
+  link_args: rom_link_args,
+  link_depends: rom_link_deps,
+  dependencies: [
+    chip_info_h,
+    sw_lib_flash_ctrl,
+    sw_lib_pinmux,
+    sw_lib_gpio,
+    sw_lib_hmac,
+    sw_lib_spi_device,
+    sw_lib_uart,
+    sw_lib_log,
+  ],
+)
+
+boot_rom_embedded = custom_target(
   'boot_rom',
   command: make_embedded_target,
+  input: boot_rom_elf,
   output: make_embedded_target_outputs,
   build_by_default: true,
-  input: executable(
-    'boot_rom',
-    sources: [
-      'boot_rom.c',
-      'bootstrap.c',
-      'crt0.S'
-    ],
-    name_suffix: 'elf',
-    link_args: rom_link_args,
-    link_depends: rom_link_deps,
-    dependencies: [
-      chip_info_h,
-      sw_lib_flash_ctrl,
-      sw_lib_pinmux,
-      sw_lib_gpio,
-      sw_lib_hmac,
-      sw_lib_spi_device,
-      sw_lib_uart,
-      sw_lib_log,
-    ],
-  ),
 )
+
+custom_target(
+  'boot_rom_export',
+  command: export_embedded_target,
+  input: [boot_rom_elf, boot_rom_embedded],
+  output: 'boot_rom_export',
+  build_always_stale: true,
+  build_by_default: true,
+)
+
diff --git a/sw/device/examples/hello_usbdev/meson.build b/sw/device/examples/hello_usbdev/meson.build
index 506cfca..970f780 100644
--- a/sw/device/examples/hello_usbdev/meson.build
+++ b/sw/device/examples/hello_usbdev/meson.build
@@ -2,21 +2,32 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-custom_target(
+hello_usbdev_elf = executable(
+  'hello_usbdev',
+  sources: ['hello_usbdev.c'],
+  name_suffix: 'elf',
+  dependencies: [
+    sw_lib_gpio,
+    sw_lib_irq,
+    sw_lib_uart,
+    sw_lib_usb,
+    riscv_crt,
+  ],
+)
+
+hello_usbdev_embedded = custom_target(
   'hello_usbdev',
   command: make_embedded_target,
+  input: hello_usbdev_elf,
   output: make_embedded_target_outputs,
   build_by_default: true,
-  input: executable(
-    'hello_usbdev',
-    sources: ['hello_usbdev.c'],
-    name_suffix: 'elf',
-    dependencies: [
-      sw_lib_gpio,
-      sw_lib_irq,
-      sw_lib_uart,
-      sw_lib_usb,
-      riscv_crt,
-    ],
-  ),
+)
+
+custom_target(
+  'hello_usbdev_export',
+  command: export_embedded_target,
+  input: [hello_usbdev_elf, hello_usbdev_embedded],
+  output: 'hello_usbdev_export',
+  build_always_stale: true,
+  build_by_default: true,
 )
diff --git a/sw/device/examples/hello_world/meson.build b/sw/device/examples/hello_world/meson.build
index dd3dd56..4fc0065 100644
--- a/sw/device/examples/hello_world/meson.build
+++ b/sw/device/examples/hello_world/meson.build
@@ -2,22 +2,33 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-custom_target(
-  'hello_world',  
+hello_world_elf = executable(
+  'hello_world',
+  sources: ['hello_world.c'],
+  name_suffix: 'elf',
+  dependencies: [
+    sw_lib_pinmux,
+    sw_lib_gpio,
+    sw_lib_irq,
+    sw_lib_spi_device,
+    sw_lib_uart,
+    riscv_crt,
+  ],
+)
+
+hello_world_embedded = custom_target(
+  'hello_world',
   command: make_embedded_target,
+  input: hello_world_elf,
   output: make_embedded_target_outputs,
   build_by_default: true,
-  input: executable(
-    'hello_world',
-    sources: ['hello_world.c',],
-    name_suffix: 'elf',
-    dependencies: [
-      sw_lib_pinmux,
-      sw_lib_gpio,
-      sw_lib_irq,
-      sw_lib_spi_device,
-      sw_lib_uart,
-      riscv_crt,
-    ],
-  ),
+)
+
+custom_target(
+  'hello_world_export',
+  command: export_embedded_target,
+  input: [hello_world_elf, hello_world_embedded],
+  output: 'hello_world_export',
+  build_always_stale: true,
+  build_by_default: true,
 )
diff --git a/sw/device/meson.build b/sw/device/meson.build
index 5516a8a..fb08245 100644
--- a/sw/device/meson.build
+++ b/sw/device/meson.build
@@ -11,7 +11,7 @@
 # These definitions should only be available to directories which define executables.
 make_embedded_target_outputs = ['@BASENAME@.bin', '@BASENAME@.dis', '@BASENAME@.vmem']
 make_embedded_target = [
-  prog_python, meson.source_root() + '/util/embedded_target.py',
+  prog_python, meson.source_root() / 'util/embedded_target.py',
   '--objcopy', prog_objcopy,
   '--srec_cat', prog_srec_cat,
   '--objdump', prog_objdump,
@@ -20,6 +20,16 @@
   '--outdir', '@OUTDIR@',
 ]
 
+# Arguments for custom_target, for copying a completed |make_embedded_target| into
+# the appropriate location under $BIN_DIR.
+export_embedded_target = [
+  meson.source_root() / 'util/export_target.sh',
+  dev_bin_dir,
+  'sw/device',
+  '@OUTDIR@',
+  '@INPUT@',
+]
+
 subdir('boot_rom')
 subdir('examples')
 subdir('tests')
diff --git a/sw/device/tests/flash_ctrl/meson.build b/sw/device/tests/flash_ctrl/meson.build
index aa2f5d1..5072db9 100644
--- a/sw/device/tests/flash_ctrl/meson.build
+++ b/sw/device/tests/flash_ctrl/meson.build
@@ -2,21 +2,33 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-custom_target(
+flash_test_elf = executable(
+  'flash_test',
+  sources: ['flash_test.c'],
+  name_suffix: 'elf',
+  dependencies: [
+    sw_lib_flash_ctrl,
+    sw_lib_gpio,
+    sw_lib_irq,
+    sw_lib_uart,
+    riscv_crt,
+  ],
+)
+
+flash_test_embedded = custom_target(
   'flash_test',
   command: make_embedded_target,
+  input: flash_test_elf,
   output: make_embedded_target_outputs,
   build_by_default: true,
-  input: executable(
-    'flash_test',
-    sources: ['flash_test.c'],
-    name_suffix: 'elf',
-    dependencies: [
-      sw_lib_flash_ctrl,
-      sw_lib_gpio,
-      sw_lib_irq,
-      sw_lib_uart,
-      riscv_crt,
-    ],
-  ),
 )
+
+custom_target(
+  'flash_test_export',
+  command: export_embedded_target,
+  input: [flash_test_elf, flash_test_embedded],
+  output: 'flash_test',
+  build_always_stale: true,
+  build_by_default: true,
+)
+
diff --git a/sw/device/tests/hmac/meson.build b/sw/device/tests/hmac/meson.build
index 60aacd2..fe289b2 100644
--- a/sw/device/tests/hmac/meson.build
+++ b/sw/device/tests/hmac/meson.build
@@ -2,21 +2,33 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-custom_target(
+sha256_test_elf = executable(
+  'sha256_test',
+  sources: ['sha256_test.c'],
+  name_suffix: 'elf',
+  dependencies: [
+    sw_lib_flash_ctrl,
+    sw_lib_hmac,
+    sw_lib_irq,
+    sw_lib_uart,
+    riscv_crt,
+  ],
+)
+
+sha256_test_embedded = custom_target(
   'sha256_test',
   command: make_embedded_target,
+  input: sha256_test_elf,
   output: make_embedded_target_outputs,
   build_by_default: true,
-  input: executable(
-    'sha256_test',
-    sources: ['sha256_test.c'],
-    name_suffix: 'elf',
-    dependencies: [
-      sw_lib_flash_ctrl,
-      sw_lib_hmac,
-      sw_lib_irq,
-      sw_lib_uart,
-      riscv_crt,
-    ],
-  ),
 )
+
+custom_target(
+  'sha256_test_export',
+  command: export_embedded_target,
+  input: [sha256_test_elf, sha256_test_embedded],
+  output: 'sha256_test',
+  build_always_stale: true,
+  build_by_default: true,
+)
+
diff --git a/sw/device/tests/rv_timer/meson.build b/sw/device/tests/rv_timer/meson.build
index 29328fa..9a4d81d 100644
--- a/sw/device/tests/rv_timer/meson.build
+++ b/sw/device/tests/rv_timer/meson.build
@@ -2,20 +2,31 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-custom_target(
+rv_timer_test_elf = executable(
+  'rv_timer_test',
+  sources: ['rv_timer_test.c'],
+  name_suffix: 'elf',
+  dependencies: [
+    sw_lib_irq,
+    sw_lib_rv_timer,
+    sw_lib_uart,
+    riscv_crt,
+  ],
+)
+
+rv_timer_test_embedded = custom_target(
   'rv_timer_test',
   command: make_embedded_target,
+  input: rv_timer_test_elf,
   output: make_embedded_target_outputs,
   build_by_default: true,
-  input: executable(
-    'rv_timer_test',
-    sources: ['rv_timer_test.c'],
-    name_suffix: 'elf',
-    dependencies: [
-      sw_lib_irq,
-      sw_lib_rv_timer,
-      sw_lib_uart,
-      riscv_crt,
-    ],
-  ),
+)
+
+custom_target(
+  'rv_timer_test_export',
+  command: export_embedded_target,
+  input: [rv_timer_test_elf, rv_timer_test_embedded],
+  output: 'rv_timer_test',
+  build_always_stale: true,
+  build_by_default: true,
 )
diff --git a/sw/host/meson.build b/sw/host/meson.build
index 10356b0..e347df5 100644
--- a/sw/host/meson.build
+++ b/sw/host/meson.build
@@ -2,5 +2,15 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
+# Arguments for custom_target, for copying a completed host binary into
+# the appropriate location under $BIN_DIR.
+export_host_target = [
+  meson.source_root() / 'util/export_target.sh',
+  host_bin_dir,
+  'sw/host',
+  '@OUTDIR@',
+  '@INPUT@',
+]
+
 subdir('vendor')
 subdir('spiflash')
diff --git a/sw/host/spiflash/meson.build b/sw/host/spiflash/meson.build
index 8ea5d96..8028b9f 100644
--- a/sw/host/spiflash/meson.build
+++ b/sw/host/spiflash/meson.build
@@ -2,7 +2,7 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-executable(
+spiflash_bin = executable(
   'spiflash',
   sources: [
     'ftdi_spi_interface.cc',
@@ -17,3 +17,13 @@
   ],
   native: true,
 )
+
+custom_target(
+  'spiflash_export',
+  output: 'spiflash_export',
+  command: export_host_target,
+  input: spiflash_bin,
+  build_always_stale: true,
+  build_by_default: true,
+)
+
diff --git a/util/export_target.sh b/util/export_target.sh
new file mode 100755
index 0000000..d178a0b
--- /dev/null
+++ b/util/export_target.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+set -e
+
+# export_target.sh is intended to be invoked by Meson, to work around Meson's
+# limitations for custom_target() (it is not possible to specify more than one
+# command) and limitations on string handling (Meson does not provide
+# standard string manipulation functions like trim_prefix()).
+#
+# This script does not use build_consts.sh, but relies on Meson to supply that
+# information.
+
+platform_bin_dir="$1"; shift
+meson_src_dir_prefix="$1"; shift
+meson_src_dir="$1"; shift
+
+target_bin_dir="$platform_bin_dir/${meson_src_dir#"$meson_src_dir_prefix"}"
+
+mkdir -p "$target_bin_dir"
+cp $@ "$target_bin_dir"
diff --git a/util/make_build_bin.sh b/util/make_build_bin.sh
deleted file mode 100755
index 8c02ae0..0000000
--- a/util/make_build_bin.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-set -e
-
-# make_build_bin.sh takes the unstructured contents of $OBJ_DIR and copies them
-# into the stable file structure of $BIN_DIR. By default, this script will skip
-# any subdirectory of $OBJ_DIR unknown to it, but setting $MUST_COPY_ALL will
-# cause trigger a hard error if any $OBJ_DIR subdir is missing.
-
-. util/build_consts.sh
-
-for platform in "${PLATFORMS[@]}"; do
-  obj_dir="$(sw_obj_dir "$platform")"
-  echo "Copying object directory $obj_dir."
-  if [[ ! -d "$obj_dir" ]]; then
-    if [[ -z ${MUST_COPY_ALL+x} ]]; then
-      echo "Error: Object directory for $platform does not exist; skipping."
-      continue
-    else
-      echo "Error: Object directory for $platform does not exist; aborting."
-      exit 1
-    fi
-  fi
-
-  bin_dir="$(sw_bin_dir "$platform")"
-  # NOTE: This find excludes all directory paths with '@' symbols in them, which
-  # are used by Meson to indicate unexported build artifacts, like .o and .a files.
-  for path in $(find "$obj_dir/sw/device" -type f -regex '[^@]+'); do
-    # NOTE: The '#' substitution operator strips the prefix $obj_root from $path.
-    rel_dir="$(dirname "${path#"$obj_dir/sw/device/"}")"
-    mkdir -p "$bin_dir/$rel_dir"
-    cp "$path" "$bin_dir/$rel_dir"
-  done
-
-  # TODO: "Host" binaries must be copied separately. Currently, Meson will compile
-  # them once per platform, even though they are the same for all platforms.
-  # As such, we copy them from the first object directory we encounter.
-  if [[ -z ${found_host_bins+x} ]]; then
-    host_obj_dir="$obj_dir/sw/host"
-    if [[ ! -d "$host_obj_dir" ]]; then
-      continue
-    fi
-    echo "Copying host binaries from $host_obj_dir."
-    for path in $(find "$host_obj_dir" -type f -regex '[^@]+'); do
-      rel_dir="$(dirname "${path#$host_obj_dir}")"
-      mkdir -p "$HOST_BIN_DIR/$rel_dir"
-      cp "$path" "$HOST_BIN_DIR/$rel_dir"
-    done
-    found_host_bins=true
-  fi
-done