Permalink
Browse files

Initial implementation of readlink -f.

Addresses issue #96.

It uses the Python function os.path.realpath(), which we discovered has
different semantics than C's realpath().

Run demo with:

$ demo/readlink-demo.sh all

Run failing "gold" test with:

$ test/gold.sh readlink-case

- Changed the gold.sh test framework to add $REPO_ROOT/bin to the $PATH
  so we can test Oil's readlink against the one on the system.

Co-authored-by: Nicolas Hahn <nicolas@stonespring.org>
  • Loading branch information...
Andy Chu and nicolashahn committed Jun 11, 2018
1 parent 3c2a7c2 commit be9f859548ba3dc5e2019091814b9f54bc4e8658
Showing with 102 additions and 4 deletions.
  1. +3 −0 bin/oil.py
  2. +1 −1 build/common.sh
  3. +1 −0 demo/readlink-demo-data.txt
  4. +14 −0 demo/readlink-demo-target.sh
  5. +32 −0 demo/readlink-demo.sh
  6. +18 −0 gold/readlink.sh
  7. +11 −3 test/gold.sh
  8. +22 −0 tools/readlink.py
View
@@ -88,6 +88,7 @@ def _tlog(msg):
from tools import deps
from tools import osh2oil
from tools import readlink
log = util.log
@@ -592,6 +593,8 @@ def AppBundleMain(argv):
return 0
elif main_name == 'false':
return 1
elif main_name == 'readlink':
return readlink.main(main_argv)
else:
raise args.UsageError('Invalid applet name %r.' % main_name)
View
@@ -19,7 +19,7 @@ readonly PY27=Python-2.7.13
readonly PREPARE_DIR=_devbuild/cpython-full
# Used by scripts/run.sh and opy/build.sh
readonly OIL_SYMLINKS=(oil osh oshc sh wok boil true false)
readonly OIL_SYMLINKS=(oil osh oshc sh wok boil true false readlink)
readonly OPY_SYMLINKS=(opy opyc)
@@ -0,0 +1 @@
--- this is data.txt ---
@@ -0,0 +1,14 @@
#!/bin/bash
echo '--- readlink-demo-data.sh ---'
echo '$0 =' $0
#cat $(dirname $0)/readlink-demo-data.txt
script=$(readlink -f $0)
#script=$(readlink $0)
#script=$(bin/readlink -f $0)
echo "script = $script"
cat $(dirname $script)/readlink-demo-data.txt
View
@@ -0,0 +1,32 @@
#!/bin/bash
#
# Usage:
# ./readlink-demo.sh <function name>
set -o nounset
set -o pipefail
set -o errexit
two-file-symlinks() {
# NOTE: Two levels of symlinks requires readlink -f, not readlink.
ln -s -f $PWD/demo/readlink-demo-target.sh /tmp/demo.sh
ln -s -f /tmp/demo.sh /tmp/demo-level2.sh
/tmp/demo-level2.sh
}
dir-symlink() {
ln -s -f --no-target-directory $PWD/demo/ /tmp/oil-demo
/tmp/oil-demo/readlink-demo-target.sh
}
all() {
echo 'Two files:'
two-file-symlinks
echo
echo 'Dirs:'
dir-symlink
echo
}
"$@"
View
@@ -0,0 +1,18 @@
#!/bin/bash
#
# Usage:
# ./readlink.sh <function name>
set -o nounset
set -o pipefail
#set -o errexit
dir-does-not-exist() {
readlink -f /nonexistent
echo $?
readlink -f /nonexistent/foo
echo $?
}
"$@"
View
@@ -14,13 +14,18 @@ set -o errexit
source test/common.sh # for $OSH
# Runs an command (argv) the normal way (with its shebang) and then with
# OSH, and compares the stdout and exit code.
#
# Also puts $PWD/bin on the front of $PATH, in order to read bin/readlink
# and so forth.
_compare() {
set +o errexit
"$@" >_tmp/shebang.txt
local expected_status=$?
$OSH "$@" >_tmp/osh.txt
PATH="$PWD/bin:$PATH" $OSH "$@" >_tmp/osh.txt
local osh_status=$?
set -o errexit
@@ -87,7 +92,7 @@ and-or() { _compare gold/and-or.sh test-simple; }
comments() { _compare gold/comments.sh; }
readonly_() { _compare gold/readonly.sh; }
export() { _compare gold/export.sh; }
export-case() { _compare gold/export.sh; }
glob() { _compare gold/glob.sh; }
no-op() { _compare scripts/count.sh; }
complex-here-docs() { _compare gold/complex-here-docs.sh; }
@@ -109,6 +114,9 @@ declare() { _compare gold/declare.sh demo; }
# Needs declare -p
scope() { _compare gold/scope.sh; }
readlink-case() {
_compare gold/readlink.sh dir-does-not-exist
}
readonly -a PASSING=(
# FLAKY: This one differs by timestamp
@@ -120,7 +128,7 @@ readonly -a PASSING=(
comments
readonly_
export
export-case
glob
no-op
complex-here-docs
View
@@ -0,0 +1,22 @@
#!/usr/bin/python
from __future__ import print_function
"""
readlink.py
"""
import os
from core import args, util
SPEC = args.BuiltinFlags()
SPEC.ShortFlag('-f')
def main(argv):
arg, i = SPEC.Parse(argv)
if not arg.f:
util.error("-f must be passed")
return 1
for arg in argv[i:]:
res = os.path.realpath(arg)
print(res)
return 0

0 comments on commit be9f859

Please sign in to comment.