Speed up realpath() with dynamically loaded C extension

On systems that support both C compiling and dynamic loading, we can
speed up `realpath()` (where most time in rbenv is spent) by replacing
it with a dynamically loaded bash builtin.

When `make -C src` is called in the project's root,
`libexec/rbenv-realpath.dylib` will be created. If it exists, rbenv will
attempt to load it as a builtin command. If it fails, execution will
fall back to the old `realpath()` shell function.
This commit is contained in:
Mislav Marohnić 2014-01-03 21:25:18 +01:00
parent 13a474c4e9
commit 16c7eb4135
5 changed files with 89 additions and 1 deletions

2
.gitignore vendored
View file

@ -4,3 +4,5 @@
/versions /versions
/sources /sources
/cache /cache
/libexec/*.dylib
/src/*.o

View file

@ -19,6 +19,7 @@ if [ -z "$RBENV_COMMAND" ]; then
exit 1 exit 1
fi fi
if ! enable -f "${BASH_SOURCE%/*}"/rbenv-realpath.dylib realpath 2>/dev/null; then
READLINK=$(type -p greadlink readlink | head -1) READLINK=$(type -p greadlink readlink | head -1)
if [ -z "$READLINK" ]; then if [ -z "$READLINK" ]; then
echo "rbenv: cannot find readlink - are you missing GNU coreutils?" >&2 echo "rbenv: cannot find readlink - are you missing GNU coreutils?" >&2
@ -42,13 +43,14 @@ realpath() {
echo "$(pwd)/$name" echo "$(pwd)/$name"
cd "$cwd" cd "$cwd"
} }
fi
IFS=: hook_paths=($RBENV_HOOK_PATH) IFS=: hook_paths=($RBENV_HOOK_PATH)
shopt -s nullglob shopt -s nullglob
for path in "${hook_paths[@]}"; do for path in "${hook_paths[@]}"; do
for script in "$path/$RBENV_COMMAND"/*.bash; do for script in "$path/$RBENV_COMMAND"/*.bash; do
echo $(realpath $script) realpath "$script"
done done
done done
shopt -u nullglob shopt -u nullglob

10
src/Makefile Normal file
View file

@ -0,0 +1,10 @@
SHOBJ_LDFLAGS = -dynamiclib -current_version 1.0
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
../libexec/rbenv-realpath.dylib: realpath.o
$(CC) $(CFLAGS) $(SHOBJ_LDFLAGS) -o $@ realpath.o
clean:
rm -f *.o ../libexec/*.dylib

31
src/bash.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef __BASH_H__
#define __BASH_H__
#define EXECUTION_SUCCESS 0
#define EXECUTION_FAILURE 1
#define EX_USAGE 258
#define BUILTIN_ENABLED 1
typedef struct word_desc {
char *word;
int flags;
} WORD_DESC;
typedef struct word_list {
struct word_list *next;
WORD_DESC *word;
} WORD_LIST;
typedef int sh_builtin_func_t(WORD_LIST *);
struct builtin {
char *name;
sh_builtin_func_t *function;
int flags;
char * const *long_doc;
const char *short_doc;
char *unused;
};
#endif

43
src/realpath.c Normal file
View file

@ -0,0 +1,43 @@
#include "bash.h"
#include <stdlib.h>
#include <stdio.h>
int realpath_builtin(list)
WORD_LIST *list;
{
int es;
char *realbuf, *p;
if (list == 0) {
// builtin_usage();
return (EX_USAGE);
}
for (es = EXECUTION_SUCCESS; list; list = list->next) {
p = list->word->word;
realbuf = realpath(p, NULL);
if (realbuf == NULL) {
es = EXECUTION_FAILURE;
// builtin_error("%s: cannot resolve: %s", p, strerror(errno));
} else {
printf("%s\n", realbuf);
free(realbuf);
}
}
return es;
}
char *realpath_doc[] = {
"Display each PATHNAME argument, resolving symbolic links. The exit status",
"is 0 if each PATHNAME was resolved; non-zero otherwise.",
(char *)NULL
};
struct builtin realpath_struct = {
"realpath",
realpath_builtin,
BUILTIN_ENABLED,
realpath_doc,
"realpath pathname [pathname...]",
0
};