[meson] Make Meson generate artifacts in the style of #650.
This change refactors where Meson emits its Ninja files to make the
design proposed by imphil in #650 possible. In particular, there are now
two build directories:
- build-out, which has an unstable directory structure, is where Meson
builds actually occur.
- build-bin, which has a stable directory structure, is where finished
build outputs are copied by ci/make_build_bin.sh.
util/make_build_bin.sh is a simple script that copies finished build
artifacts out of build-out into build-bin, meant to be invoked during
CI.
util/build_consts.sh contains build directory structure definitions,
which other scripts can source to access build directories in a portable
manner.
A followup change will add scripts to invoke fusesoc to cause top-level
hardware artifacts to be build into build-out and build-bin.
Signed-off-by: Miguel Young de la Sota <mcyoung@google.com>
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index fbe09f4..35495d6 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -133,9 +133,12 @@
--force
displayName: 'Install toolchain'
- bash: |
+ export BUILD_ROOT="$(Build.ArtifactStagingDirectory)"
+ . util/build_consts.sh
./meson_init.sh -f
- ninja -C build-verilator all
- ninja -C build-fpga all
+ ninja -C "$(sw_obj_dir sim-verilator)" all
+ ninja -C "$(sw_obj_dir fpga)" all
+ util/make_build_bin.sh
displayName: 'Build embedded targets'
- job: "deprecated_make_build"
diff --git a/ci/run_verilator_pytest.sh b/ci/run_verilator_pytest.sh
index 02d4bef..7c90abc 100755
--- a/ci/run_verilator_pytest.sh
+++ b/ci/run_verilator_pytest.sh
@@ -4,8 +4,10 @@
# SPDX-License-Identifier: Apache-2.0
set -e
+. util/build_consts.sh
+
readonly VERILATED_SYSTEM_DEFAULT="build/lowrisc_systems_top_earlgrey_verilator_0.1/sim-verilator/Vtop_earlgrey_verilator"
-readonly SW_BUILD_DEFAULT="build-verilator"
+readonly SW_BUILD_DEFAULT="$(sw_obj_dir sim)"
VERILATED_SYSTEM_PATH="${VERILATED_SYSTEM_PATH:-$VERILATED_SYSTEM_DEFAULT}"
SW_BUILD_PATH="${SW_BUILD_PATH:-$SW_BUILD_DEFAULT}"
@@ -18,10 +20,10 @@
)
if [[ ! -z ${MAKE_BUILD+x} ]]; then
- BOOT_ROM_TARGET="sim/boot_rom/rom.vmem"
- TEST_TARGETS=("sim/tests/flash_ctrl/sw.vmem"
- "sim/tests/hmac/sw.vmem"
- "sim/tests/rv_timer/sw.vmem"
+ BOOT_ROM_TARGET="sw/device/sim/boot_rom/rom.vmem"
+ TEST_TARGETS=("sw/device/sim/tests/flash_ctrl/sw.vmem"
+ "sw/device/sim/tests/hmac/sw.vmem"
+ "sw/device/sim/tests/rv_timer/sw.vmem"
)
fi
@@ -32,8 +34,8 @@
set +e
set -x
pytest -s test/systemtest/functional_verilator_test.py \
- --test_bin "$SW_BUILD_PATH/sw/device/${target}" \
- --rom_bin "$SW_BUILD_PATH/sw/device/${BOOT_ROM_TARGET}" \
+ --test_bin "$SW_BUILD_PATH/${target}" \
+ --rom_bin "$SW_BUILD_PATH/${BOOT_ROM_TARGET}" \
--verilator_model "$VERILATED_SYSTEM_PATH"
if [[ $? == 0 ]]; then
PASS_TARGETS=("${PASS_TARGETS[@]}" "${target}")
diff --git a/meson.build b/meson.build
index 6607110..400b906 100644
--- a/meson.build
+++ b/meson.build
@@ -9,7 +9,7 @@
error('target option not set. Please run meson with a valid build target option.')
endif
-if target == 'verilator'
+if target == 'sim-verilator'
# TODO: Consider using extra args array if using this flag globally is no
# longer OK.
add_project_arguments('-DSIMULATION', language: 'c')
@@ -48,7 +48,7 @@
# RISCV linker parameters.
riscv_linkfile = files(['sw/device/exts/common/link.ld'])
-riscv_link_args = ['-Wl,-T,@0@/@1@'.format('..', riscv_linkfile[0])]
+riscv_link_args = ['-Wl,-T,@0@/@1@'.format(meson.source_root(), riscv_linkfile[0])]
riscv_link_deps = [riscv_linkfile]
# RISCV CRT parameters
@@ -57,7 +57,7 @@
# Additional arguments for utility in charge of generating bin and vmem outputs
# These variables are expected to be used in custom_target rules.
embedded_target_output = ['@BASENAME@.bin', '@BASENAME@.dis', '@BASENAME@.vmem']
-embedded_target_args = [prog_python, '../util/embedded_target.py',
+embedded_target_args = [prog_python, meson.source_root() + '/util/embedded_target.py',
'--objcopy', prog_objcopy, '--srec_cat', prog_srec_cat, '--objdump', prog_objdump,
'--input', '@INPUT@', '--basename', '@BASENAME@', '--outdir', '@OUTDIR@']
diff --git a/meson_init.sh b/meson_init.sh
index fbf6d61..f3204e9 100755
--- a/meson_init.sh
+++ b/meson_init.sh
@@ -6,11 +6,13 @@
set -o errexit
set -o pipefail
set -o nounset
-set -x
-readonly BUILD_DIR_PREFIX="build"
-readonly TARGET_VERILATOR="verilator"
-readonly TARGET_FPGA="fpga"
+. util/build_consts.sh
+
+echo "Detected \$REPO_TOP at $REPO_TOP."
+echo "Object directory set at $OBJ_DIR."
+echo "Binary directory set at $BIN_DIR."
+echo
function usage() {
cat << USAGE
@@ -57,17 +59,16 @@
fi
if [[ "${FLAGS_force}" == true ]]; then
- for target_suffix in "${TARGET_VERILATOR}" "${TARGET_FPGA}"; do
- rm -rf "${BUILD_DIR_PREFIX}-${target_suffix}"
- done
+ rm -rf $BUILD_ROOT/build-*
fi
if [[ ! -n "${FLAGS_reconfigure}" ]] ; then
- for target_suffix in "${TARGET_VERILATOR}" "${TARGET_FPGA}"; do
- if [[ -d "${BUILD_DIR_PREFIX}-${target_suffix}" ]]; then
+ for platform in "${PLATFORMS[@]}"; do
+ obj_dir="$(sw_obj_dir "$platform")"
+ if [[ -d "$obj_dir" ]]; then
usage >&2
- echo "Error: ${BUILD_DIR_PREFIX}-${target_suffix} already exists. " \
- "Remove directory, or rerun $0 with the -r option" >&2
+ echo "Error: $obj_dir already exists. Remove directory, or rerun $0 " \
+ "with the -r option" >&2
exit 1
fi
done
@@ -99,19 +100,13 @@
perl -pi -e 's#-I[^/][^@ ]+ # #g' -- "$ninja_file"
}
-# configure_meson $target generates a build directory at build-$target.
-function configure_meson() {
- local target="$1"
- local build_dir="${BUILD_DIR_PREFIX}-$target"
-
- mkdir -p "$build_dir"
+for platform in ${PLATFORMS[@]}; do
+ obj_dir="$(sw_obj_dir "$platform")"
+ mkdir -p "$obj_dir"
meson ${FLAGS_reconfigure} \
- -Dtarget="$target" \
+ -Dtarget="$platform" \
--cross-file="$CROSS_FILE" \
--buildtype=plain \
- "$build_dir"
- purge_includes "$build_dir"
-}
-
-configure_meson "${TARGET_VERILATOR}"
-configure_meson "${TARGET_FPGA}"
+ "$obj_dir"
+ purge_includes "$obj_dir"
+done
diff --git a/meson_options.txt b/meson_options.txt
index e78fb4c..fd89ba6 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1 +1 @@
-option('target', type : 'combo', choices: ['verilator', 'fpga', 'undef'], value : 'undef')
+option('target', type : 'combo', choices: ['sim-verilator', 'fpga', 'undef'], value : 'undef')
diff --git a/sw/device/boot_rom/meson.build b/sw/device/boot_rom/meson.build
index b84fb45..e053cef 100644
--- a/sw/device/boot_rom/meson.build
+++ b/sw/device/boot_rom/meson.build
@@ -12,7 +12,7 @@
# ROM linker parameters.
rom_linkfile = files(['rom_link.ld'])
-rom_link_args = ['-Wl,-T,@0@/@1@'.format('..', rom_linkfile[0])]
+rom_link_args = ['-Wl,-T,@0@/@1@'.format(meson.source_root(), rom_linkfile[0])]
rom_link_deps = [rom_linkfile]
custom_target(
diff --git a/util/build_consts.sh b/util/build_consts.sh
new file mode 100644
index 0000000..d0a6632
--- /dev/null
+++ b/util/build_consts.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+# This file provides common definitions for build output locations in the
+# OpenTitan repository; scripts that wish to use it should |source| it at their
+# start.
+#
+# OpenTitan has two build directories:
+# - $OBJ_DIR, which contains all outputs and intermediates of the build
+# process, with an unstable directory structure.
+# - $BIN_DIR, which contains "executable" outputs of the build process, such as
+# binaries, tests, and bitstreams. It has a stable directory structure.
+#
+# $OBJ_DIR and $BIN_DIR are the subdirectories build-out and build-bin of
+# $BUILD_ROOT, which can be configured to any desired directory. Build artifacts
+# can be cleaned out by running |rm -rf $BUILD_ROOT/build-*|.
+
+# We cannot rely on the location of this file for anything, since it is meant to
+# be sourced, not executed. As such, we use git to compute $REPO_TOP.
+readonly REPO_TOP="$(git rev-parse --show-toplevel)"
+BUILD_ROOT="${BUILD_ROOT:-"$REPO_TOP"}"
+
+readonly OBJ_DIR="$BUILD_ROOT/build-out"
+readonly BIN_DIR="$BUILD_ROOT/build-bin"
+
+# PLATFORMS is an array of all of the "device platforms" which OpenTitan
+# software can be built for. These include:
+# - 'sim-verilator', i.e., Verilator.
+# - 'fpga', i.e., a NexysVideo FPGA board.
+readonly PLATFORMS=(
+ 'sim-verilator'
+ 'fpga'
+)
+
+# sw_obj_dir takes a platform name as an argument and produces a path to a
+# subdirectory of $OBJ_DIR where its build action artifacts should be written.
+#
+# The output of this function should be considered scratch space and not stable.
+function sw_obj_dir() {
+ echo "$OBJ_DIR/sw/$1"
+}
+
+# sw_bin_dir takes a platform name as an argument and produces a path to the
+# subdirectory of $BIN_DIR where its completed build outputs should be written.
+function sw_bin_dir() {
+ echo "$BIN_DIR/sw/device/$1"
+}
+
+# $HOST_BIN_DIR is a subdirectory of $BIN_DIR where host build outputs (i.e.,
+# compiled programs that should run on a host workstation or server) should be
+# written.
+HOST_BIN_DIR="$BIN_DIR/sw/host"
diff --git a/util/fpga/splice_nexysvideo.sh b/util/fpga/splice_nexysvideo.sh
index 9546055..578740b 100755
--- a/util/fpga/splice_nexysvideo.sh
+++ b/util/fpga/splice_nexysvideo.sh
@@ -14,9 +14,10 @@
# lowrisc_systems_top_earlgrey_nexysvideo_0.1.splice.bit
set -e
-BUILD_DIR=build-fpga
-TARGET_PREFIX="$BUILD_DIR/sw/device/boot_rom/boot_rom"
-#TARGET_PREFIX="sw/${BUILD_DIR}/rom"
+. util/build_consts.sh
+
+BUILD_DIR="$(sw_obj_dir fpga)"
+TARGET_PREFIX="/sw/device/boot_rom/boot_rom"
FPGA_BUILD_DIR=build/lowrisc_systems_top_earlgrey_nexysvideo_0.1/synth-vivado/
FPGA_BIT_NAME=lowrisc_systems_top_earlgrey_nexysvideo_0.1
diff --git a/util/make_build_bin.sh b/util/make_build_bin.sh
new file mode 100755
index 0000000..8c02ae0
--- /dev/null
+++ b/util/make_build_bin.sh
@@ -0,0 +1,54 @@
+#!/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
diff --git a/util/rom_chip_info.py b/util/rom_chip_info.py
index 93cadf6..b353ef9 100755
--- a/util/rom_chip_info.py
+++ b/util/rom_chip_info.py
@@ -53,8 +53,10 @@
outdir.mkdir(parents=True, exist_ok=True)
out_path = outdir / "chip_info.h"
-
- repo = Repo(search_parent_directories=True)
+ # This file may invoked from some random place outside the repository, so
+ # we need to make sure to do a git lookup relative to *this* file.
+ this_dir = Path(__file__).resolve().parent
+ repo = Repo(path=str(this_dir), search_parent_directories=True)
repo_info = repo.head.object.hexsha
now = datetime.now()