Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/tools/vibec.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,28 @@ int main(int argc, char *argv[]) {
}
strcat(lib_file, ".so");

const char *prefix = getenv("PREFIX");
if (!prefix) {
prefix = "/usr/local";
}

char rpath[512];
#ifdef __APPLE__
/*
* On macOS the runtime looks for libvibelang via @rpath. Embed both the
* install prefix and the plugin directory so users do not need to specify
* additional linker flags.
*/
snprintf(rpath, sizeof(rpath),
"-Wl,-rpath,%s/lib -Wl,-rpath,@loader_path", prefix);
#else
snprintf(rpath, sizeof(rpath), "-Wl,-rpath,%s/lib", prefix);
#endif

char cmd[512];
snprintf(cmd, sizeof(cmd),
"gcc -shared -fPIC %s -o %s -lvibelang",
output_file, lib_file);
"gcc -shared -fPIC %s -o %s -lvibelang %s",
output_file, lib_file, rpath);
INFO("Building shared library %s", lib_file);
if (options.verbose) {
INFO("Running: %s", cmd);
Expand Down
6 changes: 6 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ target_link_libraries(test_llm_integration
add_test(NAME test_llm_integration
COMMAND test_llm_integration)

# Test that vibec embeds the correct rpath in generated shared libraries
add_executable(test_vibec_rpath
unit/test_vibec_rpath.c
)
add_test(NAME test_vibec_rpath COMMAND test_vibec_rpath)

# Create test data directory during build
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/tests/unit/data)

Expand Down
73 changes: 73 additions & 0 deletions tests/unit/test_vibec_rpath.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>

static int run_cmd(const char *cmd) {
int ret = system(cmd);
if (ret != 0) {
fprintf(stderr, "Command failed: %s\n", cmd);
}
return ret;
}

static int test_vibec_rpath(void) {
printf("Running vibec rpath test...\n");

/* Determine install prefix (build directory parent) */
char prefix[PATH_MAX];
if (!realpath("..", prefix)) {
perror("realpath");
return 1;
}

char env_var[PATH_MAX + 10];
snprintf(env_var, sizeof(env_var), "PREFIX=%s", prefix);
putenv(env_var);

/* Create a simple vibe source */
const char *src = "tests/unit/data/rpath_test.vibe";
FILE *f = fopen(src, "w");
if (!f) {
perror("fopen");
return 1;
}
fputs("type Joke = Meaning<String>(\"a short humorous line\");\n", f);
fputs("fn tellJoke() -> Joke {\n", f);
fputs(" prompt \"Tell me a short joke.\";\n", f);
fputs("}\n", f);
fclose(f);

/* Compile with vibec */
char cmd[PATH_MAX * 2];
snprintf(cmd, sizeof(cmd), "../bin/vibec %s > /dev/null", src);
if (run_cmd(cmd) != 0) {
return 1;
}

/* Check generated shared library for runpath */
const char *lib = "tests/unit/data/rpath_test.so";
#ifdef __APPLE__
snprintf(cmd, sizeof(cmd),
"otool -l %s | grep -A2 RPATH | grep %s/lib > /dev/null", lib, prefix);
#else
snprintf(cmd, sizeof(cmd),
"readelf -d %s | grep -E '(RUNPATH|RPATH).*%s/lib' > /dev/null", lib,
prefix);
#endif
if (run_cmd(cmd) != 0) {
printf("rpath not found\n");
return 1;
}

/* Cleanup */
unlink("tests/unit/data/rpath_test.c");
unlink("tests/unit/data/rpath_test.so");
unlink(src);

printf("vibec rpath test passed!\n");
return 0;
}

int main(void) { return test_vibec_rpath(); }
Loading