initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# requires:
# make
# example usage
# locations:
# from bsp repo root:
# common/bsp_pinconfig/scripts/regen_bsp_libs.sh
# from remote location: (requires $AMSDK environment variable -- {boards_sfe} can be whatever you named the root of the bsp repo )
# $AMSDK/{boards_sfe}/common/bsp_pinconfig/scripts/regen_bsp_libs.sh -r $AMSDK/{boards_sfe}
# arguments:
# [-r $BSP_ROOT] path to bsp root optional -- defaults to the current directory
# (should be specified when calling script remotely)
# [-b $BOARDS_FILE] path to boards file optional -- defaults to all supported bsp boards
# (for now boards must still have source files located in the bsp repo)
# setup
set -e
set -o errexit
echo "" 1>&2
# get enclosing directory
DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
# defaults
BSP_ROOT=.
BOARDS_FILE=$DIR/configuration/boards.sh
# handle arguments
while getopts ":r:b:" opt; do
case $opt in
r) BSP_ROOT="$OPTARG"
;;
b) BOARDS_FILE="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" 1>&2
;;
esac
done
# verify bsp root
echo "Using \$BSP_ROOT=$BSP_ROOT" 1>&2
VFILE=$BSP_ROOT/README.md
if [ -f "$VFILE" ];
then
echo "\$BSP_ROOT verification passed" 1>&2
else
echo "\$BSP_ROOT verification failed" 1>&2
exit 1
fi
# load in boards to handle
echo "Using \$BOARDS_FILE=$BOARDS_FILE" 1>&2
source $BOARDS_FILE
echo "" 1>&2
for value in $BOARDS
do
echo "Cleaning all examples for: $value" 1>&2
# https://unix.stackexchange.com/questions/86722/how-do-i-loop-through-only-directories-in-bash
# https://stackoverflow.com/questions/4515866/iterate-through-subdirectories-in-bash
# https://stackoverflow.com/questions/9018723/what-is-the-simplest-way-to-remove-a-trailing-slash-from-each-parameter
# shopt -s nullglob
for f in $BSP_ROOT/$value/examples/*/; do
if [[ "$f" = "*" ]]; then continue; fi # protect from empty directories
if [[ -d "$f" && ! -L "$f" ]]; then
# # $f is a directory and is not a symlink
# echo "Removing: $f/gcc/bin"
# rm -rf $f/gcc/bin
echo "cleaning ${f%/}"
make -f ${f%/}/gcc/Makefile clean PROJECTPATH=${f%/}
fi
done
done
@@ -0,0 +1 @@
export BOARDS="edge edge2 artemis_module artemis_thing_plus redboard_artemis redboard_artemis_atp redboard_artemis_nano artemis_dk"
@@ -0,0 +1,107 @@
{
"copy": [
{
"from": "devices",
"to": "targets/TARGET_Ambiq_Micro/sdk/devices",
"ignore": [
"am_devices_da14581.c",
"am_devices_da14581.h",
"am_devices_em9304.c",
"am_devices_em9304.h",
"am_devices_fireball.c",
"am_devices_fireball.h",
"am_devices_mb85rc256v.c",
"am_devices_mb85rc256v.h",
"am_devices_mb85rs1mt.c",
"am_devices_mb85rs1mt.h",
"am_devices_mspi_atxp032.c",
"am_devices_mspi_atxp032.h",
"am_devices_mspi_psram_aps6404l.c",
"am_devices_mspi_psram_aps6404l.h",
"am_devices_mspi_rm67162.c",
"am_devices_mspi_rm67162.h",
"am_devices_mspi_s25fs064s.c",
"am_devices_mspi_s25fs064s.h",
"am_devices_spiflash.c",
"am_devices_spiflash.h"
]
},
{
"from": "utils",
"to": "targets/TARGET_Ambiq_Micro/sdk/utils",
"ignore": [
"am_util_faultisr.c",
"am_util_regdump.c"
]
},
{
"from": "mcu",
"to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/sdk/mcu",
"ignore": [
"apollo",
"apollo2",
"apollo3p",
"Makefile",
"gcc",
"iar",
"keil"
]
},
{
"from": "CMSIS",
"to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/sdk/CMSIS",
"ignore": [
"apollo1.h",
"apollo2.h",
"apollo3p.h",
"system_apollo1.h",
"system_apollo2.h",
"system_apollo3p.h",
"system_apollo1.c",
"system_apollo2.c",
"system_apollo3p.c",
"startup_apollo1.s",
"startup_apollo2.s",
"startup_apollo3.s",
"startup_apollo3p.s",
"arm_math.h",
"cmsis_armcc.h",
"cmsis_armclang.h",
"cmsis_compiler.h",
"cmsis_gcc.h",
"cmsis_iccarm.h",
"cmsis_version.h",
"core_cm4.h",
"mpu_armv7.h",
"arm_cortexM4l_math.lib",
"arm_cortexM4lf_math.lib",
"iar_cortexM4l_math.a",
"iar_cortexM4lf_math.a",
"libarm_cortexM4l_math.a"
]
},
{"from": "boards_sfe/artemis_dk/bsp", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/TARGET_SFE_ARTEMIS_DK/bsp", "ignore": ["gcc", "bsp_pins.src"]},
{"from": "boards_sfe/artemis_thing_plus/bsp", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/TARGET_SFE_ARTEMIS_THING_PLUS/bsp", "ignore": ["gcc", "bsp_pins.src"]},
{"from": "boards_sfe/edge/bsp", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/TARGET_SFE_EDGE/bsp", "ignore": ["gcc", "bsp_pins.src"]},
{"from": "boards_sfe/edge2/bsp", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/TARGET_SFE_EDGE2/bsp", "ignore": ["gcc", "bsp_pins.src"]},
{"from": "boards_sfe/redboard_artemis/bsp", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/TARGET_SFE_ARTEMIS/bsp", "ignore": ["gcc", "bsp_pins.src"]},
{"from": "boards_sfe/redboard_artemis_atp/bsp", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/TARGET_SFE_ARTEMIS_ATP/bsp", "ignore": ["gcc", "bsp_pins.src"]},
{"from": "boards_sfe/redboard_artemis_nano/bsp", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/TARGET_SFE_ARTEMIS_NANO/bsp", "ignore": ["gcc", "bsp_pins.src"]},
{"from": "boards_sfe/common/third_party/hm01b0", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/COMPONENT_hm01b0/hm01b0", "ignore": []},
{"from": "boards_sfe/common/third_party/lis2dh12", "to": "targets/TARGET_Ambiq_Micro/TARGET_Apollo3/COMPONENT_lis2dh12/lis2dh12", "ignore": []}
],
"generate": {
"pincfgs": {
"peripherals": {
"src": "{_sdk}/boards_sfe/common/support/apollo3/peripherals/pins.src",
"dest": "{_mbed}/targets/TARGET_Ambiq_Micro/TARGET_Apollo3/device/PeripheralPinConfigs",
"guard": "_APOLLO3_PERIPHERAL_PIN_CONFIGS_H_",
"bgaguard": "AM_PACKAGE_BGA",
"prefix": "AP3_PER"
}
}
}
}
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# requires:
# - 'make' available at the command line
# - 'arm-none-eabi-xxx' available at the command line (preferred version is q4-2018-major)
# example usage
# locations:
# from bsp repo root:
# common/bsp_pinconfig/scripts/regen_bsp_libs.sh
# from remote location: (requires $AMSDK environment variable -- {boards_sfe} can be whatever you named the root of the bsp repo )
# $AMSDK/{boards_sfe}/common/bsp_pinconfig/scripts/regen_bsp_libs.sh -r $AMSDK/{boards_sfe}
# arguments:
# [-r $BSP_ROOT] path to bsp root optional -- defaults to the current directory
# (should be specified when calling script remotely)
# [-b $BOARDS_FILE] path to boards file optional -- defaults to all supported bsp boards
# (for now boards must still have source files located in the bsp repo)
# setup
set -e
set -o errexit
echo "" 1>&2
# get enclosing directory
DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
# defaults
BSP_ROOT=.
BOARDS_FILE=$DIR/configuration/boards.sh
# handle arguments
while getopts ":r:b:" opt; do
case $opt in
r) BSP_ROOT="$OPTARG"
;;
b) BOARDS_FILE="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" 1>&2
;;
esac
done
# verify bsp root
echo "Using \$BSP_ROOT=$BSP_ROOT" 1>&2
VFILE=$BSP_ROOT/README.md
if [ -f "$VFILE" ];
then
echo "\$BSP_ROOT verification passed" 1>&2
else
echo "\$BSP_ROOT verification failed" 1>&2
exit 1
fi
# load in boards to handle
echo "Using \$BOARDS_FILE=$BOARDS_FILE" 1>&2
source $BOARDS_FILE
echo "" 1>&2
for value in $BOARDS
do
echo "Regenerating bsp library for: $value" 1>&2
make -C $BSP_ROOT/$value/bsp/gcc
done
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# requires:
# - python3 available at the command line
# example usage
# locations:
# from bsp repo root:
# common/bsp_pinconfig/scripts/regen_bsp_pins.sh
# from remote location: (requires $AMSDK environment variable -- {boards_sfe} can be whatever you named the root of the bsp repo )
# $AMSDK/{boards_sfe}/common/bsp_pinconfig/scripts/regen_bsp_pins.sh -r $AMSDK/{boards_sfe}
# arguments:
# [-r $BSP_ROOT] path to bsp root optional -- defaults to the current directory
# (should be specified when calling script remotely)
# [-b $BOARDS_FILE] path to boards file optional -- defaults to all supported bsp boards
# (for now boards must still have source files located in the bsp repo)
# setup
set -e
set -o errexit
echo "" 1>&2
# get enclosing directory
DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
# defaults
BSP_ROOT=.
BOARDS_FILE=$DIR/configuration/boards.sh
# handle arguments
while getopts ":r:b:" opt; do
case $opt in
r) BSP_ROOT="$OPTARG"
;;
b) BOARDS_FILE="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" 1>&2
;;
esac
done
# verify bsp root
echo "Using \$BSP_ROOT=$BSP_ROOT" 1>&2
VFILE=$BSP_ROOT/README.md
if [ -f "$VFILE" ];
then
echo "\$BSP_ROOT verification passed" 1>&2
else
echo "\$BSP_ROOT verification failed" 1>&2
exit 1
fi
# load in boards to handle
echo "Using \$BOARDS_FILE=$BOARDS_FILE" 1>&2
source $BOARDS_FILE
# generate bsp files for every board
echo "" 1>&2
for value in $BOARDS
do
echo "Regenerating bsp_pins files for: $value" 1>&2
$BSP_ROOT/common/bsp_pinconfig/pinconfig.py $BSP_ROOT/$value/bsp/bsp_pins.src h > $BSP_ROOT/$value/bsp/am_bsp_pins.h
$BSP_ROOT/common/bsp_pinconfig/pinconfig.py $BSP_ROOT/$value/bsp/bsp_pins.src c > $BSP_ROOT/$value/bsp/am_bsp_pins.c
done
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# requires:
# - see requirements of source scripts
# example usage
# locations:
# from bsp repo root:
# common/bsp_pinconfig/scripts/regen_bsps.sh
# from remote location: (requires $AMSDK environment variable -- {boards_sfe} can be whatever you named the root of the bsp repo )
# $AMSDK/{boards_sfe}/common/bsp_pinconfig/scripts/regen_bsps.sh -r $AMSDK/{boards_sfe}
# arguments:
# [-r $BSP_ROOT] path to bsp root optional -- defaults to the current directory
# (should be specified when calling script remotely)
# [-b $BOARDS_FILE] path to boards file optional -- defaults to all supported bsp boards
# (for now boards must still have source files located in the bsp repo)
# setup
set -e
set -o errexit
echo "" 1>&2
# get enclosing directory
DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
# defaults
BSP_ROOT=.
BOARDS_FILE=$BSP_ROOT/common/tools_sfe/scripts/configuration/boards.sh
# handle arguments
while getopts ":r:b:" opt; do
case $opt in
r) BSP_ROOT="$OPTARG"
;;
b) BOARDS_FILE="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" 1>&2
;;
esac
done
# regen pins and libs
$BSP_ROOT/common/tools_sfe/scripts/regen_bsp_pins.sh -r $BSP_ROOT -b $BOARDS_FILE # regenerates source (.h and .c) files from the bsp_pins.src files
$BSP_ROOT/common/tools_sfe/scripts/regen_bsp_libs.sh -r $BSP_ROOT -b $BOARDS_FILE # regenerates library archive files from bsp source (.h and .c) files
@@ -0,0 +1,103 @@
#!/usr/bin/env python3.8
# requires python3.8
import argparse
import json
import shutil
import os
import subprocess
from braceexpand import braceexpand as expand
# ***********************************************************************************
#
# Main function
#
# ***********************************************************************************
def main():
# load configuration
with open(args.config, 'r') as fin:
config = json.loads(fin.read())
# handle copy steps
for step in config['copy']:
src_path = os.path.join(args.sdk, step['from'])
dest_path = os.path.join(args.mbed, step['to'])
ignore_patterns = step['ignore']
print("copy")
print("from '" + src_path + "'")
print("to '" + dest_path + "'")
print("\texcluding:")
print(ignore_patterns)
print()
shutil.rmtree(dest_path, ignore_errors=True)
shutil.copytree(src_path, dest_path, ignore=shutil.ignore_patterns(*ignore_patterns), dirs_exist_ok=True)
# handle pincfg generation
print('generate pincfgs:')
def absolutify(path):
new_path = path
new_path = new_path.replace('{_mbed}', args.mbed)
new_path = new_path.replace('{_sdk}', args.sdk)
return new_path
selectors = ['c', 'h']
for name, job in config['generate']['pincfgs'].items():
print('\t', name)
for selector in selectors:
results = subprocess.run( [
'python',
absolutify('{_sdk}/boards_sfe/common/bsp_pinconfig/pinconfig.py'),
absolutify(job['src']), selector,
'-g', job['guard'],
'-p', job['prefix'],
'-b', job['bgaguard']
],
capture_output=True)
with open(absolutify(job['dest']) + '.' + selector, 'wb') as fout:
fout.write(results.stdout)
errors = str(results.stderr)
if errors != "b''":
print('error: ', str(results.stderr))
exit()
# ******************************************************************************
#
# Main program flow
#
# ******************************************************************************
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Utility to generate GCC flag and symbol files from mbed build profiles')
parser.add_argument('-m', '--mbed', dest='mbed', required=True, help='path to root of mbed')
parser.add_argument('-c', '--config', dest='config', required=False, default='./boards_sfe/common/tools_sfe/scripts/configuration/mbed-config.json', help='path to the json configuration file')
parser.add_argument('-s', '--sdk', dest='sdk', required=False, default='.', help='path to root of AmbiqSuiteSDK')
parser.add_argument('-v', '--verbose', default=0, help='enable verbose output', action='store_true')
args = parser.parse_args()
# Create print function for verbose output if caller deems it: https://stackoverflow.com/questions/5980042/how-to-implement-the-verbose-or-v-option-into-a-script
if args.verbose:
def verboseprint(*args):
# Print each argument separately so caller doesn't need to
# stuff everything to be printed into a single string
for arg in args:
print(arg, end='', flush=True)
print()
else:
verboseprint = lambda *a: None # do-nothing function
def twopartprint(verbosestr, printstr):
if args.verbose:
print(verbosestr, end = '')
print(printstr)
main()
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# requires:
# a script to aid in upgrading the AmbiqSuite SDK
# # get enclosing directory
# DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") # can handle symlinks but fails on mac
DIR=$(dirname -- "$BASH_SOURCE") # assumes no symlinks.... (for mac compatibility)
# import sh-realpath
source $DIR/utility/sh-realpath/realpath.sh
# defaults
SDK_ROOT=$DIR/../../../..
UPGRADE_ROOT=
# handle arguments
while getopts ":r:u:" opt; do
case $opt in
r) SDK_ROOT="$OPTARG"
;;
u) UPGRADE_ROOT="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" 1>&2
;;
esac
done
# ensure user provided -u option
if [ -z "$UPGRADE_ROOT" ]
then
echo "User must provide -u option (path to upgrade sdk directory)"
exit 1
fi
# make a temporary directory to hold the old sdk
TMP_DIR=$(mktemp -d -t ambiqsuite_sdk_upgrade_tmp)
echo "created temporary directory $TMP_DIR"
# get the sdk folder name (and verify)
SDK_ROOT=$(realpath $SDK_ROOT)
SDK_NAME=$(basename $SDK_ROOT)
echo "Got SDK name: $SDK_NAME"
VFILE=$SDK_ROOT/VERSION.txt
if [ -f "$VFILE" ];
then
echo "\$SDK_ROOT verification passed" 1>&2
else
echo "\$SDK_ROOT verification failed" 1>&2
exit 1
fi
# get the upgrade folder (and verify)
UPGRADE_ROOT=$(realpath $UPGRADE_ROOT)
echo "Got upgrade SDK path: $UPGRADE_ROOT"
VFILE=$UPGRADE_ROOT/VERSION.txt
if [ -f "$VFILE" ];
then
echo "\$UPGRADE_ROOT verification passed" 1>&2
else
echo "\$UPGRADE_ROOT verification failed" 1>&2
exit 1
fi
# copy sdk to temporary dir (being sure to keep hidden files)
cp -r $SDK_ROOT/. $TMP_DIR
# remove old directory
rm -rf $SDK_ROOT
# copy the upgrade to the SDK_ROOT
cp -r $UPGRADE_ROOT $SDK_ROOT
# copy important directories from the old SDK to the upgrade
cp -r $TMP_DIR/.git* $SDK_ROOT
cp -r $TMP_DIR/README.md $SDK_ROOT
cp -r $TMP_DIR/boards_sfe $SDK_ROOT
# clean up temporary folder
rm -rf $TMP_DIR
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# this script relies on the environment variable $AMSDK being set to the root of your AmbiqSuite SDK
# use it when you have a pre-built binary file to upload onto a board
# default variables
BINFILE=.
ASB_UPLOAD_BAUD=115200
PORT=COM4
# immutables
COMMONPATH=$AMSDK/boards_sfe/common
PYTHON3=python3
AMBIQ_BIN2BOARD=$COMMONPATH/tools_sfe/asb/asb.py
BINPATH=temp_
# handle arguments
while getopts ":p:f:b:u:" opt; do
case $opt in
p) PORT="$OPTARG"
;;
f) BINFILE="$OPTARG"
;;
b) ASB_UPLOAD_BAUD="$OPTARG"
;;
u) UPLOADER="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
;;
esac
done
printf "Uploading file with Ambiq Secure Bootloader:\n%s\n\n" "$BINFILE"
$PYTHON3 $AMBIQ_BIN2BOARD --bin $BINFILE --load-address-blob 0x20000 --magic-num 0xCB -o $BINPATH --version 0x0 --load-address-wired 0xC000 -i 6 --options 0x1 -b $ASB_UPLOAD_BAUD -port $PORT -r 2 -v
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Michael Kropat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,18 @@
.PHONY: test
test: lint unit-test
.PHONY: lint
lint:
-shellcheck realpath.sh
-checkbashisms realpath.sh
.PHONY: unit-test
unit-test: t/*
t/%: force
bash "$@"
dash "$@"
.PHONY: force
force: ;
@@ -0,0 +1,43 @@
# sh-realpath
*A portable, pure shell implementation of realpath*
Copy the functions in [realpath.sh](realpath.sh) into your shell script to
avoid introducing a dependency on either `realpath` or `readlink -f`, since:
* `realpath` does not come installed by default
* `readlink -f` **is not portable** to OS-X ([relevant man page](https://developer.apple.com/library/mac/documentation/Darwin/Reference/Manpages/man1/readlink.1.html))
## Usage
$ source ./realpath.sh
$ realpath /proc/self
/proc/2772
Or we can get tricky:
$ cd /tmp
$ mkdir -p somedir/targetdir somedir/anotherdir
$ ln -s somedir somedirlink
$ ln -s somedir/anotherdir/../anotherlink somelink
$ ln -s targetdir/targetpath somedir/anotherlink
$ realpath .///somedirlink/././anotherdir/../../somelink
/tmp/somedir/targetdir/targetpath
## API
Note: unlike `realpath(1)`, these functions take no options; **do not** use `--` to escape any arguments
| Function | Description
| --------------------------------- | -------------
| <pre>realpath PATH</pre> | Resolve all symlinks to `PATH`, then output the canonicalized result
| <pre>resolve_symlinks PATH</pre> | If `PATH` is a symlink, follow it as many times as possible; output the path of the first non-symlink found
| <pre>canonicalize_path PATH</pre> | Output absolute path that `PATH` refers to, resolving any relative directories (`.`, `..`) in `PATH` and any symlinks in `PATH`'s ancestor directories
### readlink Emulation
`realpath.sh` includes optional readlink emulation. It exposes a `readlink`
function that calls the system `readlink(1)` if it exists. Otherwise it uses
`stat(1)` to emulate the same functionality. In contrast to the functions in
the previous section, you may pass `--` as the first argument, since you may be
calling the system `readlink(1)`.
@@ -0,0 +1,111 @@
#!/bin/sh
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
resolve_symlinks() {
_resolve_symlinks "$1"
}
_resolve_symlinks() {
_assert_no_path_cycles "$@" || return
local dir_context path
path=$(readlink -- "$1")
if [ $? -eq 0 ]; then
dir_context=$(dirname -- "$1")
_resolve_symlinks "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@"
else
printf '%s\n' "$1"
fi
}
_prepend_dir_context_if_necessary() {
if [ "$1" = . ]; then
printf '%s\n' "$2"
else
_prepend_path_if_relative "$1" "$2"
fi
}
_prepend_path_if_relative() {
case "$2" in
/* ) printf '%s\n' "$2" ;;
* ) printf '%s\n' "$1/$2" ;;
esac
}
_assert_no_path_cycles() {
local target path
target=$1
shift
for path in "$@"; do
if [ "$path" = "$target" ]; then
return 1
fi
done
}
canonicalize_path() {
if [ -d "$1" ]; then
_canonicalize_dir_path "$1"
else
_canonicalize_file_path "$1"
fi
}
_canonicalize_dir_path() {
(cd "$1" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
local dir file
dir=$(dirname -- "$1")
file=$(basename -- "$1")
(cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}
# Optionally, you may also want to include:
### readlink emulation ###
readlink() {
if _has_command readlink; then
_system_readlink "$@"
else
_emulated_readlink "$@"
fi
}
_has_command() {
hash -- "$1" 2>/dev/null
}
_system_readlink() {
command readlink "$@"
}
_emulated_readlink() {
if [ "$1" = -- ]; then
shift
fi
_gnu_stat_readlink "$@" || _bsd_stat_readlink "$@"
}
_gnu_stat_readlink() {
local output
output=$(stat -c %N -- "$1" 2>/dev/null) &&
printf '%s\n' "$output" |
sed "s/^[^]* -> \(.*\)/\1/
s/^'[^']*' -> '\(.*\)'/\1/"
# FIXME: handle newlines
}
_bsd_stat_readlink() {
stat -f %Y -- "$1" 2>/dev/null
}
@@ -0,0 +1,130 @@
#!/bin/sh
. ./realpath.sh
setUp() {
_test_previousdir=$PWD
_test_workingdir=$(mktemp -d -t sh-realpath.XXXXXX)
cd -P -- "$_test_workingdir"
}
tearDown() {
cd -- "$_test_previousdir"
rm -rf -- "$_test_workingdir"
}
it_outputs_pwd_when_passed_zero_args() {
local output
output=$(canonicalize_path)
assertTrue '`canonicalize_path` succeeds' $?
assertEquals 'outputs $PWD' "$PWD/" "$output"
}
it_outputs_absolute_path_to_target_when_passed_the_name_of_a_non_existent_file() {
local output
output=$(canonicalize_path non_existent_file)
assertTrue '`canonicalize_path non_existent_file` succeeds' $?
assertEquals 'output is "$PWD/non_existent_file"' "$PWD/non_existent_file" "$output"
}
it_returns_an_error_when_passed_a_path_in_a_non_existent_dir() {
local output
output=$(canonicalize_path non_existent_dir/somepath 2>&1)
assertFalse '`canonicalize_path non_existent_dir/somepath` fails' $?
assertNull 'no output' "$output"
}
it_outputs_absolute_path_when_passed_the_name_of_a_path_in_a_subdir() {
local output
mkdir somedir
output=$(canonicalize_path somedir/somepath)
assertTrue '`canonicalize_path somedir/somepath` succeeds' $?
assertEquals 'output is "$PWD/somedir/somepath"' "$PWD/somedir/somepath" "$output"
}
it_returns_an_error_when_passed_a_path_in_a_symlink_to_nowhere() {
ln -s non/existent/dir somelink
canonicalize_path somelink/somepath
assertFalse '`canonicalize_path somelink/somepath` fails' $?
}
it_returns_the_target_dir_when_passed_a_symlink_to_another_dir() {
local output
mkdir somedir
ln -s somedir somelink
output=$(canonicalize_path somelink)
assertTrue '`canonicalize_path somelink` succeeds' $?
assertEquals 'output is "$PWD/somedir"' "$PWD/somedir" "$output"
}
it_returns_absolute_path_of_target_directory_when_passed_a_path_in_a_symlink_to_another_dir() {
local output
mkdir somedir
ln -s somedir somelink
output=$(canonicalize_path somelink/somepath)
assertTrue '`canonicalize_path somelink/somepath` succeeds' $?
assertEquals 'output is "$PWD/somedir/somepath"' "$PWD/somedir/somepath" "$output"
}
it_collapses_current_dir_references() {
local output
output=$(canonicalize_path ././somepath)
assertTrue '`canonicalize_path ././somepath` succeeds' $?
assertEquals 'output is "$PWD/somepath"' "$PWD/somepath" "$output"
}
it_collapses_extra_slashes() {
local output
output=$(canonicalize_path .///somepath)
assertTrue '`canonicalize_path .///somepath` succeeds' $?
assertEquals 'output is "$PWD/somepath"' "$PWD/somepath" "$output"
}
it_collapses_parent_dir_references() {
local output
mkdir -p somedir/anotherdir
output=$(canonicalize_path somedir/anotherdir/../../somepath)
assertTrue '`canonicalize_path somedir/anotherdir/../../somepath` succeeds' $?
assertEquals 'output is "$PWD/somepath"' "$PWD/somepath" "$output"
}
##### Test Harness #####
# suite() -- find and register tests to be run
# Derived from Gary Bernhardt's screencast #68
# (https://www.destroyallsoftware.com/screencasts/catalog/test-driving-shell-scripts)
suite() {
local name tests
tests=$(grep ^it_ "$0" | cut -d '(' -f 1)
for name in $tests; do
suite_addTest "$name"
done
}
if hash shunit2 2>/dev/null; then
. shunit2
else
echo 'Error: shunit2(1) could not be located. Please install it on your $PATH.' >&2
exit 1
fi
@@ -0,0 +1,126 @@
#!/bin/sh
. ./realpath.sh
setUp() {
unset _system_readlink _gnu_stat_readlink _bsd_stat_readlink
_has_command() {
return 1
}
}
it_calls_system_readlink_when_has_command_readlink_is_true() {
_has_command() {
test "$1" = readlink
}
local readlink_arg1 readlink_arg2
_system_readlink() {
readlink_arg1="$1"
readlink_arg2="$2"
}
readlink -- some/path
assertEquals -- "$readlink_arg1"
assertEquals some/path "$readlink_arg2"
}
it_calls__gnu_stat_readlink_when_has_command_readlink_is_false() {
local called
_gnu_stat_readlink() {
called=1
}
readlink -- some/path
assertNotNull "_gnu_stat_readlink called" "$called"
}
it_passes_actual_arg_to__gnu_stat_readlink() {
local arg
_gnu_stat_readlink() {
arg="$1"
}
readlink -- some/path
assertEquals some/path "$arg"
}
it_passes_first_arg_to__gnu_stat_readlink_when_no_dashes() {
local arg
_gnu_stat_readlink() {
arg="$1"
}
readlink some/path
assertEquals some/path "$arg"
}
it_doesnt_call__bsd_stat_readlink_when__gnu_stat_readlink_returns_true() {
_gnu_stat_readlink() {
return 0
}
local called
_bsd_stat_readlink() {
called=1
}
readlink -- some/path
assertNull "_bsd_stat_readlink not called" "$called"
}
it_calls__bsd_stat_readlink_when__gnu_stat_readlink_returns_false() {
_gnu_stat_readlink() {
return 1
}
local called
_bsd_stat_readlink() {
called=1
}
readlink -- some/path
assertNotNull "_bsd_stat_readlink called" "$called"
}
it_passes_actual_arg_to__bsd_stat_readlink() {
_gnu_stat_readlink() {
return 1
}
local arg
_bsd_stat_readlink() {
arg="$1"
}
readlink -- some/path
assertEquals some/path "$arg"
}
##### Test Harness #####
# suite() -- find and register tests to be run
# Derived from Gary Bernhardt's screencast #68
# (https://www.destroyallsoftware.com/screencasts/catalog/test-driving-shell-scripts)
suite() {
local name tests
tests=$(grep ^it_ "$0" | cut -d '(' -f 1)
for name in $tests; do
suite_addTest "$name"
done
}
if hash shunit2 2>/dev/null; then
. shunit2
else
echo 'Error: shunit2(1) could not be located. Please install it on your $PATH.' >&2
exit 1
fi
@@ -0,0 +1,56 @@
#!/bin/sh
. ./realpath.sh
setUp() {
_test_previousdir=$PWD
_test_workingdir=$(mktemp -d -t sh-realpath.XXXXXX)
cd -P -- "$_test_workingdir"
}
tearDown() {
cd -- "$_test_previousdir"
rm -rf -- "$_test_workingdir"
}
it_outputs_pwd_when_passed_zero_args() {
local output
output=$(realpath)
assertTrue '`realpath` succeeds' $?
assertEquals 'outputs "$PWD/"' "$PWD/" "$output"
}
it_outputs_the_canonical_path_of_crazy_paths() {
local output
mkdir -p somedir/targetdir somedir/anotherdir
ln -s somedir somedirlink
ln -s somedir/anotherdir/../anotherlink somelink
ln -s targetdir/targetpath somedir/anotherlink
output=$(realpath .///somedirlink/././anotherdir/../../somelink)
assertTrue '`realpath .///somedirlink/././anotherdir/../../somelink` succeeds' $?
assertEquals 'outputs "$PWD/somedir/targetdir/targetpath"' "$PWD/somedir/targetdir/targetpath" "$output"
}
##### Test Harness #####
# suite() -- find and register tests to be run
# Derived from Gary Bernhardt's screencast #68
# (https://www.destroyallsoftware.com/screencasts/catalog/test-driving-shell-scripts)
suite() {
local name tests
tests=$(grep ^it_ "$0" | cut -d '(' -f 1)
for name in $tests; do
suite_addTest "$name"
done
}
if hash shunit2 2>/dev/null; then
. shunit2
else
echo 'Error: shunit2(1) could not be located. Please install it on your $PATH.' >&2
exit 1
fi
@@ -0,0 +1,177 @@
#!/bin/sh
. ./realpath.sh
setUp() {
_test_previousdir=$PWD
_test_workingdir=$(mktemp -d -t sh-realpath.XXXXXX)
cd -P -- "$_test_workingdir"
}
tearDown() {
cd -- "$_test_previousdir"
rm -rf -- "$_test_workingdir"
}
it_outputs_nothing_when_passed_nothing() {
local output
output=$(resolve_symlinks)
assertTrue '`resolve_symlinks` succeeds' $?
assertNull 'there is no output' "$output"
}
it_outputs_filename_when_passed_a_nonexistent_filename() {
local output
output=$(resolve_symlinks non_existent_file)
assertTrue '`resolve_symlinks non_existent_file` succeeds' $?
assertEquals 'output is "non_existent_file"' non_existent_file "$output"
}
it_outputs_filename_when_passed_a_file_that_exists() {
: >|somefile
output=$(resolve_symlinks somefile)
assertTrue '`resolve_symlinks somefile` succeeds' $?
assertEquals 'output is "somefile"' somefile "$output"
}
it_outputs_the_target_when_passed_a_symlink_to_a_file_that_doesnt_exist() {
local output
ln -s non_existent_file somelink
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "non_existent_file"' non_existent_file "$output"
}
it_outputs_the_target_when_passed_a_symlink_to_a_file_that_exists() {
local output
ln -s somefile somelink
: >|somefile
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "somefile"' somefile "$output"
}
it_outputs_the_target_when_passed_a_symlink_in_a_child_dir() {
local output
mkdir somedir
ln -s somepath somedir/somelink
output=$(resolve_symlinks somedir/somelink)
assertTrue '`resolve_symlinks somedir/somelink` succeeds' $?
assertEquals 'output is "somedir/somepath"' somedir/somepath "$output"
}
it_outputs_the_final_target_when_passed_a_symlink_to_a_symlink_to_a_file() {
local output
ln -s anotherlink somelink
ln -s somepath anotherlink
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "somepath"' somepath "$output"
}
it_outputs_the_final_target_with_path_when_passed_a_symlink_to_a_file_in_another_dir() {
local output
ln -s somedir/somepath somelink
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "somedir/somepath"' somedir/somepath "$output"
}
it_outputs_the_final_target_with_path_when_passed_a_symlink_to_a_symlink_in_another_dir() {
local output
ln -s somedir/anotherlink somelink
mkdir somedir
ln -s somepath somedir/anotherlink
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "somedir/somepath"' somedir/somepath "$output"
}
it_outputs_a_valid_path_to_the_current_dir_when_passed_a_symlink_that_has_trailing_dots() {
local output
ln -s somepath/.. somelink
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "somepath/.."' somepath/.. "$output"
}
it_outputs_a_valid_path_to_the_final_target_when_passed_a_symlink_that_references_the_parent_dir() {
local output
mkdir somedir
ln -s ../somepath somedir/somelink
output=$(resolve_symlinks somedir/somelink)
assertTrue '`resolve_symlinks somedir/somelink` succeeds' $?
assertEquals 'output is "somedir/../somepath"' somedir/../somepath "$output"
}
it_outputs_the_absolute_path_when_passed_a_symlink_to_an_absolute_path() {
local output
ln -s /some/absolute/path somelink
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "/some/absolute/path"' /some/absolute/path "$output"
}
it_outputs_the_final_target_when_passed_a_symlink_to_an_absolute_path_symlink() {
local output
ln -s $PWD/anotherlink somelink
ln -s somepath anotherlink
output=$(resolve_symlinks somelink)
assertTrue '`resolve_symlinks somelink` succeeds' $?
assertEquals 'output is "$PWD/somepath"' $PWD/somepath "$output"
}
it_returns_an_error_when_passed_a_symlink_to_a_symlink_to_a_symlink_that_points_to_the_first_symlink() {
ln -s anotherlink somelink
ln -s circularlink anotherlink
ln -s somelink circularlink
resolve_symlinks somelink
assertFalse '`resolve_symlinks somelink` fails' $?
}
##### Test Harness #####
# suite() -- find and register tests to be run
# Derived from Gary Bernhardt's screencast #68
# (https://www.destroyallsoftware.com/screencasts/catalog/test-driving-shell-scripts)
suite() {
local name tests
tests=$(grep ^it_ "$0" | cut -d '(' -f 1)
for name in $tests; do
suite_addTest "$name"
done
}
if hash shunit2 2>/dev/null; then
. shunit2
else
echo 'Error: shunit2(1) could not be located. Please install it on your $PATH.' >&2
exit 1
fi