aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@users.noreply.github.com>2020-05-07 06:11:13 -0400
committerGitHub <noreply@github.com>2020-05-07 06:11:13 -0400
commit26db5dff18a40b402d20a572953870aab549c5f2 (patch)
tree539d6a9800f41efad4ed50b1864aae1e677ccded
parent6cc450d8ff714af09374f9bc07aea8bb05f74a5c (diff)
parentde47b0d91a79651088e76d64dc4b032146203cca (diff)
downloadspmc-26db5dff18a40b402d20a572953870aab549c5f2.tar.gz
Merge pull request #33 from jhunkeler/install-name-tool
Install name tool (etc...)
-rw-r--r--.circleci/config.yml1
-rwxr-xr-x.circleci/init.sh1
-rwxr-xr-x.circleci/test_spm.sh30
-rw-r--r--CMakeLists.txt10
-rw-r--r--include/CMakeLists.txt1
-rw-r--r--include/checksum.h1
-rw-r--r--include/error_handler.h1
-rw-r--r--include/rpath.h1
-rw-r--r--include/shlib.h4
-rw-r--r--include/spm.h21
-rw-r--r--lib/CMakeLists.txt5
-rw-r--r--lib/error_handler.c1
-rw-r--r--lib/install.c1
-rw-r--r--lib/internal_cmd.c9
-rw-r--r--lib/rpath.c120
-rw-r--r--lib/shlib.c91
-rw-r--r--scripts/CMakeLists.txt18
-rw-r--r--scripts/realpath.c28
-rwxr-xr-xscripts/spmbuild74
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/framework.h112
-rw-r--r--tests/test_rpath_rpath_get.c21
23 files changed, 448 insertions, 110 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 258d452..8b16216 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -51,6 +51,7 @@ jobs:
HOMEBREW_NO_AUTO_UPDATE: 1
DYLD_INSERT_LIBRARIES: /usr/lib/libgmalloc.dylib
TEST_RESULTS: /tmp/test-results
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig:/usr/local/opt/curl/lib/pkgconfig
steps:
- checkout
diff --git a/.circleci/init.sh b/.circleci/init.sh
index 24b2f81..6a25d16 100755
--- a/.circleci/init.sh
+++ b/.circleci/init.sh
@@ -22,6 +22,7 @@ if [[ $(uname -s) == Linux ]]; then
elif [[ $(uname -s) == Darwin ]]; then
brew install cmake \
+ curl \
gnu-tar \
openssl@1.1
fi
diff --git a/.circleci/test_spm.sh b/.circleci/test_spm.sh
index 104e8fb..bd9e19e 100755
--- a/.circleci/test_spm.sh
+++ b/.circleci/test_spm.sh
@@ -18,27 +18,25 @@ spm --list
spm --search zlib
-if [[ $(uname -s) == Linux ]]; then
- spm --verbose --yes --root $PREFIX --install python
+spm --verbose --yes --root $PREFIX --install python
- set +x
- echo ACTIVATING ROOT: $PREFIX
- spm --cmd mkruntime $PREFIX > $PREFIX/bin/activate || exit 1
- source $PREFIX/bin/activate || exit 1
- echo OK!
- set -x
+set +x
+echo ACTIVATING ROOT: $PREFIX
+spm --cmd mkruntime $PREFIX > $PREFIX/bin/activate || exit 1
+source $PREFIX/bin/activate || exit 1
+echo OK!
+set -x
- which python3
+which python3
- python3 -V
+python3 -V
- python3 -c 'from sysconfig import get_config_vars; from pprint import pprint; pprint(get_config_vars())'
+python3 -c 'from sysconfig import get_config_vars; from pprint import pprint; pprint(get_config_vars())'
- python3 -m ensurepip
+python3 -m ensurepip
- pip3 --version
+pip3 --version
- pip3 install --upgrade pip setuptools
+pip3 install --upgrade pip setuptools
- pip3 --version
-fi
+pip3 --version
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 189100d..d2d1d89 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.11)
project(spm C)
+include(FindPkgConfig)
include(CTest)
include(CheckSymbolExists)
@@ -10,6 +11,15 @@ set(CMAKE_INSTALL_RPATH $ORIGIN/../lib)
enable_testing()
check_symbol_exists(strsep string.h HAVE_STRSEP)
check_symbol_exists(reallocarray stdlib.h HAVE_REALLOCARRAY)
+pkg_check_modules(OpenSSL openssl>=1.1)
+pkg_check_modules(CURL libcurl>=7.0)
+find_program(RELOC reloc)
+find_program(TAR tar)
+find_program(WHICH which)
+find_program(FILE file)
+find_program(OBJDUMP objdump)
+find_program(RSYNC rsync)
+
configure_file(${CMAKE_SOURCE_DIR}/config.h.in ${CMAKE_BINARY_DIR}/include/config.h)
add_subdirectory(lib)
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 68ca824..1009b96 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -6,6 +6,7 @@ install(
compat.h
conf.h
environment.h
+ error_handler.h
find.h
fs.h
install.h
diff --git a/include/checksum.h b/include/checksum.h
index 780ee97..5309527 100644
--- a/include/checksum.h
+++ b/include/checksum.h
@@ -4,7 +4,6 @@
#ifndef SPM_CHECKSUM_H
#define SPM_CHECKSUM_H
-char *md5sum(const char *filename);
char *sha256sum(const char *filename);
#endif //SPM_CHECKSUM_H
diff --git a/include/error_handler.h b/include/error_handler.h
index e58698e..eaf6420 100644
--- a/include/error_handler.h
+++ b/include/error_handler.h
@@ -17,6 +17,7 @@
#define SPM_ERR_MANIFEST_INVALID _SPM_ERR(7) // manifest file is invalid (no header)
#define SPM_ERR_MANIFEST_EMPTY _SPM_ERR(8) // manifest file has no data
#define SPM_ERR_PARSE _SPM_ERR(9) // general parsing error
+#define SPM_ERR_NOT_IMPLEMENTED _SPM_ERR(10) // not implemented (does exist on this platform)
extern int spmerrno;
extern const char *SPM_ERR_STRING[];
diff --git a/include/rpath.h b/include/rpath.h
index 46bdbec..2a33bea 100644
--- a/include/rpath.h
+++ b/include/rpath.h
@@ -5,6 +5,7 @@
#define SPM_RPATH_H
Process *patchelf(const char *_filename, const char *_args);
+Process *install_name_tool(const char *_filename, const char *_args);
FSTree *rpath_libraries_available(const char *root);
char *rpath_autodetect(const char *filename, FSTree *tree);
int has_rpath(const char *_filename);
diff --git a/include/shlib.h b/include/shlib.h
index 68b8487..6ae9bb1 100644
--- a/include/shlib.h
+++ b/include/shlib.h
@@ -8,7 +8,7 @@
#define SPM_SHLIB_EXEC "dumpbin"
#define SPM_SHLIB_EXEC_ARGS "/dependents"
#define SPM_SHLIB_EXTENSION ".dll"
-#elif OS_APPLE
+#elif OS_DARWIN
#define SPM_SHLIB_EXEC "/usr/bin/objdump"
#define SPM_SHLIB_EXEC_ARGS "-macho -p"
#define SPM_SHLIB_EXTENSION ".dylib"
@@ -18,6 +18,8 @@
#define SPM_SHLIB_EXTENSION ".so"
#endif
+char *objdump(const char *_filename, char *_args);
+char *shlib_rpath(const char *filename);
StrList *shlib_deps(const char *_filename);
#endif //SPM_SHLIB_H
diff --git a/include/spm.h b/include/spm.h
index 5382378..e0e713f 100644
--- a/include/spm.h
+++ b/include/spm.h
@@ -8,16 +8,24 @@
#define OS_DARWIN 0
#define OS_WINDOWS 0
#define OS_LINUX 0
+#define OS_SUPPORTED 0
#if defined(__APPLE__) && defined(__MACH__)
-#undef OS_DARWIN
-#define OS_DARWIN 1
+# undef OS_DARWIN
+# define OS_DARWIN 1
+# undef OS_SUPPORTED
+# define OS_SUPPORTED 1
+
+// TODO: Windows is not supported at all yet, so OS_SUPPORTED is untouched
#elif defined(_WIN32)
-#undef OS_WINDOWS
-#define OS_WINDOWS 1
+# undef OS_WINDOWS
+# define OS_WINDOWS 1
+
#elif defined(__linux) || defined(__linux__)
-#undef OS_LINUX
-#define OS_LINUX 1
+# undef OS_LINUX
+# define OS_LINUX 1
+# undef OS_SUPPORTED
+# define OS_SUPPORTED 1
#endif
#include <ctype.h>
@@ -32,7 +40,6 @@
#include <string.h>
#include <time.h>
#include <sys/stat.h>
-#include <openssl/md5.h>
#include <openssl/sha.h>
#if !OS_WINDOWS
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 2185ef4..96147e1 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,6 +1,8 @@
include_directories(
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
+ ${OpenSSL_INCLUDE_DIRS}
+ ${CURL_INCLUDE_DIRS}
)
set(libspm_src
@@ -39,7 +41,8 @@ add_library(libspm SHARED $<TARGET_OBJECTS:libspm_obj>)
add_library(libspm_static STATIC $<TARGET_OBJECTS:libspm_obj>)
-target_link_libraries(libspm crypto ssl curl)
+target_link_directories(libspm PUBLIC ${OpenSSL_LIBRARY_DIRS} ${CURL_LIBRARY_DIRS})
+target_link_libraries(libspm ${OpenSSL_LIBRARIES} ${CURL_LIBRARIES})
if (LINUX)
target_link_libraries(libspm rt)
endif()
diff --git a/lib/error_handler.c b/lib/error_handler.c
index aa48274..2ccb4e7 100644
--- a/lib/error_handler.c
+++ b/lib/error_handler.c
@@ -14,6 +14,7 @@ const char *SPM_ERR_STRING[] = {
"Manifest has no header",
"Manifest has no data",
"Parsing error",
+ "Not implemented",
NULL,
};
diff --git a/lib/install.c b/lib/install.c
index 32348e2..9fb80c2 100644
--- a/lib/install.c
+++ b/lib/install.c
@@ -278,6 +278,7 @@ int spm_do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
if (package_path == NULL) {
free(package_path);
free(package_localpath);
+ // TODO: set spmerrno here
exit(1);
}
fetched = 1;
diff --git a/lib/internal_cmd.c b/lib/internal_cmd.c
index 6f6b1c6..a859b5b 100644
--- a/lib/internal_cmd.c
+++ b/lib/internal_cmd.c
@@ -218,7 +218,11 @@ int mkruntime_interface(int argc, char **argv) {
runtime_set(rt, "SPM_MAN", fs->mandir);
runtime_set(rt, "SPM_LOCALSTATE", fs->localstatedir);
runtime_set(rt, "SPM_PKGCONFIG", spm_pkgconfigdir);
+#if OS_DARWIN
+ runtime_set(rt, "SPM_PKGCONFIG", "${SPM_PKGCONFIG}:${SPM_DATA}/pkgconfig");
+#elif OS_LINUX
runtime_set(rt, "SPM_PKGCONFIG", "${SPM_PKGCONFIG}:${SPM_LIB64}/pkgconfig:${SPM_DATA}/pkgconfig");
+#endif
runtime_set(rt, "SPM_META_DEPENDS", SPM_META_DEPENDS);
runtime_set(rt, "SPM_META_PREFIX_BIN", SPM_META_PREFIX_BIN);
runtime_set(rt, "SPM_META_PREFIX_TEXT", SPM_META_PREFIX_TEXT);
@@ -236,9 +240,10 @@ int mkruntime_interface(int argc, char **argv) {
runtime_set(rt, "CC", "$SPM_BIN/gcc");
}
- runtime_set(rt, "CFLAGS", "-I$SPM_INCLUDE $CFLAGS");
+ runtime_set(rt, "CFLAGS", "-I$SPM_INCLUDE");
#if OS_DARWIN
- runtime_set(rt, "LDFLAGS", "-rpath $SPM_LIB:$SPM_LIB64 -L$SPM_LIB -L$SPM_LIB64 $LDFLAGS");
+ // For now `reloc` can fix up the LC_ID_DYLIB on its own without install_name_tool
+ runtime_set(rt, "LDFLAGS", "-rpath $SPM_LIB -L$SPM_LIB");
#elif OS_LINUX
runtime_set(rt, "LDFLAGS", "-Wl,-rpath=$SPM_LIB:$SPM_LIB64 -L$SPM_LIB -L$SPM_LIB64 $LDFLAGS");
#else
diff --git a/lib/rpath.c b/lib/rpath.c
index 4d4d801..ae73574 100644
--- a/lib/rpath.c
+++ b/lib/rpath.c
@@ -5,7 +5,7 @@
/**
* Wrapper function to execute `patchelf` with arguments
- * @param _filename Path of file to modify
+ * @param _filename Path to file
* @param _args Arguments to pass to `patchelf`
* @return success=Process struct, failure=NULL
*/
@@ -32,6 +32,34 @@ Process *patchelf(const char *_filename, const char *_args) {
}
/**
+ * Wrapper function to execute `install_name_tool` with arguments
+ * @param _filename Path to file
+ * @param _args Arguments to pass to `install_name_tool`
+ * @return success=Process struct, failure=NULL
+ */
+Process *install_name_tool(const char *_filename, const char *_args) {
+ char *filename = strdup(_filename);
+ char *args = strdup(_args);
+ Process *proc_info = NULL;
+ char sh_cmd[PATH_MAX];
+ sh_cmd[0] = '\0';
+
+ strchrdel(args, SHELL_INVALID);
+ strchrdel(filename, SHELL_INVALID);
+ sprintf(sh_cmd, "install_name_tool %s %s 2>&1", args, filename);
+
+ if (SPM_GLOBAL.verbose > 1) {
+ printf(" EXEC : %s\n", sh_cmd);
+ }
+
+ shell(&proc_info, SHELL_OUTPUT, sh_cmd);
+
+ free(filename);
+ free(args);
+ return proc_info;
+}
+
+/**
* Determine whether a RPATH or RUNPATH is present in file
*
* TODO: Replace with OS-native solution(s)
@@ -40,30 +68,18 @@ Process *patchelf(const char *_filename, const char *_args) {
* @return -1=OS error, 0=has rpath, 1=not found
*/
int has_rpath(const char *_filename) {
- int result = 1; // default: not found
+ char *rpath = NULL;
- char *filename = strdup(_filename);
- if (!filename) {
+ if (_filename == NULL) {
+ spmerrno = EINVAL;
return -1;
}
- // sanitize input path
- strchrdel(filename, SHELL_INVALID);
-
- Process *pe = patchelf(filename, "--print-rpath");
- strip(pe->output);
- if (!isempty(pe->output)) {
- result = 0;
- }
- else {
- // something went wrong with patchelf other than
- // what we're looking for
- result = -1;
- }
+ if ((rpath = shlib_rpath(_filename)) == NULL) {
+ return 1;
+ };
- free(filename);
- shell_free(pe);
- return result;
+ return 0;
}
/**
@@ -78,42 +94,7 @@ char *rpath_get(const char *_filename) {
if ((has_rpath(_filename)) != 0) {
return strdup("");
}
- char *filename = strdup(_filename);
- if (!filename) {
- return NULL;
- }
- char *path = strdup(filename);
- if (!path) {
- free(filename);
- return NULL;
- }
-
- char *rpath = NULL;
-
- // sanitize input path
- strchrdel(path, SHELL_INVALID);
-
- Process *pe = patchelf(filename, "--print-rpath");
- if (pe->returncode != 0) {
- fprintf(stderr, "patchelf error: %s %s\n", path, strip(pe->output));
- return NULL;
- }
-
- rpath = (char *)calloc(strlen(pe->output) + 1, sizeof(char));
- if (!rpath) {
- free(filename);
- free(path);
- shell_free(pe);
- return NULL;
- }
-
- strncpy(rpath, pe->output, strlen(pe->output));
- strip(rpath);
-
- free(filename);
- free(path);
- shell_free(pe);
- return rpath;
+ return shlib_rpath(_filename);
}
/**
@@ -149,10 +130,19 @@ char *rpath_generate(const char *_filename, FSTree *tree) {
int rpath_set(const char *filename, const char *rpath) {
int returncode = 0;
char args[PATH_MAX];
+ Process *pe = NULL;
memset(args, '\0', PATH_MAX);
- sprintf(args, "--set-rpath '%s'", rpath); // note: rpath requires single-quotes
- Process *pe = patchelf(filename, args);
+#if OS_LINUX
+ sprintf(args, "--set-rpath '%s'", rpath);
+ pe = patchelf(filename, args);
+#elif OS_DARWIN
+ sprintf(args, "-add-rpath '%s'", rpath);
+ pe = install_name_tool(filename, args);
+#elif OS_WINDOWS
+ // TODO: assuming windows has a mechanism for changing runtime paths, do it here.
+#endif
+
if (pe != NULL) {
returncode = pe->returncode;
}
@@ -203,7 +193,7 @@ FSTree *rpath_libraries_available(const char *root) {
* @return success=relative path from `filename` to nearest lib directory, failure=NULL
*/
char *rpath_autodetect(const char *filename, FSTree *tree) {
- const char *origin = "$ORIGIN";
+ const char *origin;
char *rootdir = dirname(filename);
char *start = realpath(rootdir, NULL);
char *cwd = realpath(".", NULL);
@@ -214,6 +204,14 @@ char *rpath_autodetect(const char *filename, FSTree *tree) {
char *relative = _relative; // Pointer to relative path
size_t depth_to_root = 0;
+#if OS_DARWIN
+ origin = "@rpath";
+#elif OS_LINUX
+ origin = "$ORIGIN";
+#else
+ origin = NULL;
+#endif
+
// BUG: Perl dumps its shared library in a strange place.
// This function returns `$ORIGIN/../../../CORE` which is not what we want to see.
// TODO: We WANT to see this: `$ORIGIN/../lib/perl5/xx.xx.xx/<arch>/CORE` not just the basename()
@@ -256,7 +254,7 @@ char *rpath_autodetect(const char *filename, FSTree *tree) {
// Get the shared library name we are going to look for in the tree
char *shared_library = strlist_item(libs_wanted, i);
- // Is the the shared library in the tree?
+ // Is the shared library in the tree?
char *match = NULL;
if ((match = dirname(strstr_array(tree->files, shared_library))) != NULL) {
// Begin generating the relative path string
@@ -283,12 +281,14 @@ char *rpath_autodetect(const char *filename, FSTree *tree) {
}
}
+#if OS_LINUX
// Some programs do not require local libraries provided by SPM (i.e. libc)
// Inject "likely" defaults here
if (strlist_count(libs) == 0) {
strlist_append(libs, "$ORIGIN/../lib");
strlist_append(libs, "$ORIGIN/../lib64");
}
+#endif
// Populate result string
result = join(libs->data, ":");
diff --git a/lib/shlib.c b/lib/shlib.c
index 7eed058..091d22e 100644
--- a/lib/shlib.c
+++ b/lib/shlib.c
@@ -1,7 +1,7 @@
#include "spm.h"
#include "shlib.h"
-char *shlib_deps_objdump(const char *_filename) {
+char *objdump(const char *_filename, char *_args) {
// do not expose this function
char *filename = NULL;
char *result = NULL;
@@ -21,7 +21,7 @@ char *shlib_deps_objdump(const char *_filename) {
}
strchrdel(filename, SHELL_INVALID);
- snprintf(cmd, sizeof(cmd), "%s %s '%s'", SPM_SHLIB_EXEC, SPM_SHLIB_EXEC_ARGS, filename);
+ snprintf(cmd, sizeof(cmd), "%s %s '%s'", SPM_SHLIB_EXEC, _args, filename);
shell(&proc, SHELL_OUTPUT, cmd);
if (proc->returncode != 0) {
@@ -36,6 +36,90 @@ char *shlib_deps_objdump(const char *_filename) {
return result;
}
+char *shlib_rpath(const char *filename) {
+ char **data = NULL;
+ char *raw_data = NULL;
+ char *result = NULL;
+
+ if (filename == NULL) {
+ spmerrno = EINVAL;
+ spmerrno_cause("filename was NULL");
+ return NULL;
+ }
+
+ if ((raw_data = objdump(filename, SPM_SHLIB_EXEC_ARGS)) == NULL) {
+ return NULL;
+ }
+
+ // Split output into individual lines
+ if ((data = split(raw_data, "\n")) == NULL) {
+ free(raw_data);
+ return NULL;
+ }
+
+ // Collapse all whitespace in each line
+ // i.e. " stuff things" -> "stuff things"
+ for (size_t i = 0; data[i] != NULL; i++) {
+ data[i] = normalize_space(data[i]);
+ }
+
+ for (size_t i = 0; data[i] != NULL; i++) {
+ char **field = NULL;
+ char reason[255] = {0,};
+
+#if OS_LINUX
+ // Extract the RPATH record (second field)
+ if (startswith(data[i], "RPATH")) {
+ if ((field = split(data[i], " ")) == NULL) {
+ break;
+ }
+
+ // record library path
+ result = strdup(field[1]);
+ split_free(field);
+ break;
+ }
+#elif OS_DARWIN
+ size_t offset_name = i + 2; // how many lines to look ahead after reaching LC_RPATH
+ size_t numLines;
+ for (numLines = 0; data[numLines] != NULL; numLines++); // get line count
+
+ // Find APPLE's equivalent to RPATH on Linux
+ if (startswith(data[i], "cmd LC_RPATH")) {
+ // Don't overrun the data buffer
+ if (offset_name > numLines || data[offset_name] == NULL) {
+ break;
+ }
+
+ // split on: "path /library/path"
+ if ((field = split(data[offset_name], " ")) == NULL) {
+ sprintf(reason, "'%s' produced unreadable output at offset %zu", SPM_SHLIB_EXEC, offset_name);
+ spmerrno = SPM_ERR_PARSE;
+ spmerrno_cause(reason);
+ break;
+ }
+
+ // verify it was actually "path ..."
+ if (strcmp(field[0], "path") != 0) {
+ sprintf(reason, "'%s' produced unexpected LC_RPATH format between lines %zu:%zu", SPM_SHLIB_EXEC, i, offset_name);
+ spmerrno = SPM_ERR_PARSE;
+ spmerrno_cause(reason);
+ break;
+ }
+
+ // record library path
+ result = strdup(field[1]);
+ split_free(field);
+ break;
+ }
+#endif
+ }
+
+ free(raw_data);
+ split_free(data);
+ return result;
+}
+
StrList *shlib_deps(const char *filename) {
char **data = NULL;
char *raw_data = NULL;
@@ -48,8 +132,7 @@ StrList *shlib_deps(const char *filename) {
}
// Get output from objdump
- // TODO: use preprocessor or another function to select the correct shlib_deps_*() in the future
- if ((raw_data = shlib_deps_objdump(filename)) == NULL) {
+ if ((raw_data = objdump(filename, SPM_SHLIB_EXEC_ARGS)) == NULL) {
return NULL;
}
diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt
index a9dc74a..d24d32c 100644
--- a/scripts/CMakeLists.txt
+++ b/scripts/CMakeLists.txt
@@ -1,4 +1,20 @@
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/spmbuild
+ ${CMAKE_CURRENT_BINARY_DIR}
+ COPYONLY
+)
+
+if (APPLE)
+ add_executable(spm_realpath
+ realpath.c
+ )
+ install(
+ PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/spm_realpath
+ DESTINATION bin
+ )
+endif ()
+
install(
- PROGRAMS spmbuild
+ PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/spmbuild
DESTINATION bin
)
diff --git a/scripts/realpath.c b/scripts/realpath.c
new file mode 100644
index 0000000..15504a0
--- /dev/null
+++ b/scripts/realpath.c
@@ -0,0 +1,28 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+/**
+ * Apple loves being different. There's no `realpath` program on MacOS
+ * @param argc
+ * @param argv
+ * @return
+ */
+int main(int argc, char *argv[]) {
+ char *path;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s filename\n", argv[0]);
+ return 1;
+ }
+
+ path = realpath(argv[1], NULL);
+ if (path) {
+ puts(path);
+ free(path);
+ } else {
+ perror(argv[1]);
+ }
+
+ return errno;
+} \ No newline at end of file
diff --git a/scripts/spmbuild b/scripts/spmbuild
index a5e6300..8222849 100755
--- a/scripts/spmbuild
+++ b/scripts/spmbuild
@@ -1,12 +1,13 @@
#!/usr/bin/env bash
trap spm_build_cleanup EXIT
+THIS_SCRIPT=$(dirname ${BASH_SOURCE[0]})
function pushd() {
command pushd "$@" >/dev/null
}
function popd() {
- command popd "$@" >/dev/null
+ command popd >/dev/null
}
function _msg() {
@@ -48,8 +49,32 @@ function tar() {
$cmd "$@"
}
+function spm_debug_shell() {
+ local where
+
+ where="${1}"
+ if [[ -z "${where}" ]] || [[ ! -d "${where}" ]]; then
+ where="$(pwd)"
+ fi
+
+ pushd "${where}" || exit 1
+
+ PS1="(SPMBUILD DEBUG)$ "
+ export PS1
+
+ echo "ENTERING DEBUG SHELL"
+ env bash
+
+ # Exiting here prevents the user from leaving debug calls in their build scripts
+ # (i.e. when this function is used, no package archives will be generated)
+ exit 1
+}
+
function spm_build_initialize_stage1() {
- export SPMDEV=$(python -c "import os; print(os.path.abspath('$(dirname ${BASH_SOURCE[0]})/../cmake-build-debug/src/spm'))")
+ rp=$(which realpath 2>/dev/null \
+ || which spm_realpath 2>/dev/null \
+ || echo ./spm_realpath)
+ export SPMDEV=$($rp "${THIS_SCRIPT}/../src/spm")
export SPM=$(which spm 2>/dev/null || echo ${SPMDEV})
spm_build_check_rt_env
@@ -127,11 +152,11 @@ function spm_build_initialize_stage2() {
unset FCFLAGS
# All templates strings MUST be the same length to allow for proper relocation
- export _SPM_BUILD_ROOT_TEMPLATE="spmbuild_root_XXXXXXXXX"
- export _SPM_BUILD_RUNTIME_TEMPLATE="spmbuild_runtime_XXXXXX"
- export _SPM_BUILD_PKGDIR_TEMPLATE="spmbuild_pkgdir_XXXXXXX"
+ export _SPM_BUILD_ROOT_TEMPLATE="${TMPDIR}/spmbuild_root_XXXXXXXXX"
+ export _SPM_BUILD_RUNTIME_TEMPLATE="${TMPDIR}/spmbuild_runtime_XXXXXX"
+ export _SPM_BUILD_PKGDIR_TEMPLATE="${TMPDIR}/spmbuild_pkgdir_XXXXXXX"
- export SPM_BUILD_ROOT_BASE=$(mktemp -d -t "${_SPM_BUILD_ROOT_TEMPLATE}")
+ export SPM_BUILD_ROOT_BASE=$(mktemp -d "${_SPM_BUILD_ROOT_TEMPLATE}")
export SPM_BUILD_ROOT="${SPM_BUILD_ROOT_BASE}/${SPM_META_PREFIX_PLACEHOLDER}"
export _srcdir="${SPM_BUILD_ROOT}"
mkdir -p "${SPM_BUILD_ROOT}"
@@ -140,7 +165,7 @@ function spm_build_initialize_stage2() {
exit 1
fi
- export SPM_BUILD_RUNTIME_BASE=$(mktemp -d -t "${_SPM_BUILD_RUNTIME_TEMPLATE}")
+ export SPM_BUILD_RUNTIME_BASE=$(mktemp -d "${_SPM_BUILD_RUNTIME_TEMPLATE}")
export SPM_BUILD_RUNTIME="${SPM_BUILD_RUNTIME_BASE}/${SPM_META_PREFIX_PLACEHOLDER}"
export _runtime="${SPM_BUILD_RUNTIME}"
@@ -150,7 +175,7 @@ function spm_build_initialize_stage2() {
exit 1
fi
- export SPM_BUILD_PKGDIR_BASE=$(mktemp -d -t "${_SPM_BUILD_PKGDIR_TEMPLATE}")
+ export SPM_BUILD_PKGDIR_BASE=$(mktemp -d "${_SPM_BUILD_PKGDIR_TEMPLATE}")
export SPM_BUILD_PKGDIR="${SPM_BUILD_PKGDIR_BASE}/${SPM_META_PREFIX_PLACEHOLDER}"
export _pkgdir="${SPM_BUILD_PKGDIR}"
@@ -175,27 +200,43 @@ function spm_build_initialize_stage2() {
fi
spm_build_new_root "${SPM_BUILD_RUNTIME}"
- export LD_LIBRARY_PATH="${SPM_BUILD_RUNTIME}/lib:${SPM_BUILD_RUNTIME}/lib64"
- export LD_RUN_PATH="${LD_LIBRARY_PATH}"
- if [[ $(uname -s) == Darwin ]]; then
- DYLD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
- DYLD_RUN_PATH="${LD_RUN_PATH}"
+ LD_LIBRARY_PATH="${SPM_BUILD_RUNTIME}/lib:${SPM_BUILD_RUNTIME}/lib64"
+ LD_RUN_PATH="${LD_LIBRARY_PATH}"
+
+ if [[ $(uname -s) == Linux ]]; then
+ export LD_LIBRARY_PATH
+ export LD_RUN_PATH
+ elif [[ $(uname -s) == Darwin ]]; then
+ # Darwin is ridiculous. Don't rely on DYLD_* anything.
+ unset DYLD_LIBRARY_PATH
unset LD_LIBRARY_PATH
- unset LD_RUNPATH_PATH
+ unset LD_RUN_PATH
+ #elif # ... another OS's equivalent variable here
fi
+
export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1
export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1
}
+spm_build_cleaned=0
+export spm_build_cleaned
function spm_build_cleanup() {
- #:
+ # Don't clean up more than once
+ if (( spm_build_cleaned > 0)); then
+ return
+ fi
+
if [[ ${keep} != 0 ]]; then
- echo "Temporary storage not destroyed"
+ msg "Temporary storage not destroyed"
+ msg2 "${SPM_BUILD_ROOT_BASE}"
+ msg2 "${SPM_BUILD_RUNTIME_BASE}"
+ msg2 "${SPM_BUILD_PKGDIR_BASE}"
return
fi
[[ -d ${SPM_BUILD_ROOT_BASE} ]] && rm -rf ${SPM_BUILD_ROOT_BASE}
[[ -d ${SPM_BUILD_RUNTIME_BASE} ]] && rm -rf ${SPM_BUILD_RUNTIME_BASE}
[[ -d ${SPM_BUILD_PKGDIR_BASE} ]] && rm -rf ${SPM_BUILD_PKGDIR_BASE}
+ spm_build_cleaned=1
}
function spm_build_install() {
@@ -413,6 +454,7 @@ ls -l "${SPM_BUILD_ROOT}"
msg "Environment data"
printenv | grep -E '^SPM_' | sort
+printenv | grep -E '^[A-Z]{0,3}FLAGS' | sort
msg "Processing ${package_name}"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2bd023c..b8c042f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,13 +1,16 @@
include_directories(
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
+ ${OpenSSL_INCLUDE_DIRS}
+ ${CURL_INCLUDE_DIRS}
)
add_executable(spm
spm.c
)
-target_link_libraries(spm libspm crypto ssl curl)
+target_link_directories(spm PUBLIC ${OpenSSL_LIBRARY_DIRS} ${CURL_LIBRARY_DIRS})
+target_link_libraries(spm libspm ${OpenSSL_LIBRARIES} ${CURL_LIBRARIES})
if (LINUX)
target_link_libraries(spm libspm rt)
endif()
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e4d069c..287c37f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,6 +1,8 @@
include_directories(
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
+ ${OpenSSL_INCLUDE_DIRS}
+ ${CURL_INCLUDE_DIRS}
)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/tests)
set(CTEST_BINARY_DIRECTORY ${PROJECT_BINARY_DIR}/tests)
diff --git a/tests/framework.h b/tests/framework.h
index 9b56267..4a7a1a3 100644
--- a/tests/framework.h
+++ b/tests/framework.h
@@ -126,6 +126,118 @@ char *mock_size(size_t size, const char *fill_byte) {
return strdup(filename);
}
+#define AS_MOCK_LIB 0
+#define AS_MOCK_BIN 1
+/**
+ * Create a mock binary image (shared library, or executable)
+ *
+ * ~~~{.c}
+ * int retval;
+ *
+ * if ((retval = mock_image(AS_MOCK_LIB, "libwinning", NULL)) < 0) {
+ * fprintf(stderr, "Failed to create shared library\n");
+ * exit(retval);
+ * }
+ *
+ * if ((retval = mock_image(AS_MOCK_EXEC, "winning", (char *[]){"-L.", "-lwinning", NULL}) < 0) {
+ * fprintf(stderr, "Failed to create shared library\n");
+ * exit(retval);
+ * }
+ * ~~~
+ *
+ * @param img_type - AS_MOCK_LIB, AS_MOCK_EXEC
+ * @param img_name - library to create (e.g. "libexample" will generate "libexample.so")
+ * @param _extra_compiler_args - array of strings (may be NULL)
+ * @return
+ */
+char *mock_image(int img_type, const char *img_name, char **_extra_compiler_args, int *exit_code) {
+ char libsuffix[10] = {0,};
+ char code[255] = {0,};
+ char code_filename[FILENAME_MAX] = {0,};
+ char img_filename[FILENAME_MAX] = {0,};
+ char cmd[PATH_MAX] = {0,};
+ char *extra_compiler_args = NULL;
+ Process *proc = NULL;
+
+ if (img_name == NULL) {
+ return NULL;
+ }
+
+ if (_extra_compiler_args != NULL) {
+ extra_compiler_args = join(_extra_compiler_args, " ");
+ }
+
+ strcpy(libsuffix, SPM_SHLIB_EXTENSION);
+ sprintf(code_filename, "%s.c", img_name);
+
+ if (img_type == AS_MOCK_LIB) {
+ sprintf(code, "int %sMockFn(void) {\n return 0;\n}\n", img_name);
+ sprintf(img_filename, "%s%s", img_name, libsuffix);
+#if OS_DARWIN
+ sprintf(cmd, "gcc -o %s -fPIC -dynamiclib %s -install_name @rpath/%s -Xlinker -rpath $(pwd) '%s'",
+ img_filename, extra_compiler_args, img_filename, code_filename);
+#elif OS_LINUX
+ sprintf(cmd, "gcc -o %s -fPIC -shared %s -Wl,-rpath=$(pwd) '%s'",
+ img_filename, extra_compiler_args, code_filename);
+#elif OS_WINDOWS // TODO: UNTESTED
+#if defined (__MINGW32__)
+ sprintf(cmd, "gcc -shared -o %s -Wl,—out-implib,%s.a -Wl,—export-all-symbols -Wl,—enable-auto-image-base '%s',
+ img_filename, img_name, extra_compiler_args, code_filename);
+#elif defined (__MSC_VER)
+ sprintf(cmd, "CL /LD %s", img_filename);
+#endif // windows compiler ident
+#endif // OS_WINDOWS
+ } else if (img_type == AS_MOCK_BIN) {
+ sprintf(code, "int main(int argc, char *argv[]) {\n return 0;\n}\n");
+ sprintf(img_filename, "%s", img_name);
+#if OS_DARWIN
+ sprintf(cmd, "gcc -o %s %s -Xlinker -rpath $(pwd) '%s'",
+ img_filename, extra_compiler_args, code_filename);
+#elif OS_LINUX
+ sprintf(cmd, "gcc -o %s %s -Wl,-rpath=$(pwd) '%s'",
+ img_filename, extra_compiler_args, code_filename);
+#elif OS_WINDOWS // TODO: UNTESTED
+#if defined (__MINGW32__)
+ sprintf(cmd, "gcc -o %s %s '%s',
+ img_filename, extra_compiler_args, code_filename);
+#elif defined (__MSC_VER)
+ sprintf(cmd, "CL /Fe\"%s\" %s", img_name, img_filename);
+#endif // windows compiler ident
+#endif // OS_WINDOWS
+ } else {
+ fprintf(stderr, "Unknown image type: %d\n", img_type);
+ return NULL;
+ }
+
+ // Write sourcecode to file
+ if (mock(code_filename, code, sizeof(char), strlen(code)) == 0) {
+ return NULL;
+ }
+
+
+ if (extra_compiler_args != NULL) {
+ free(extra_compiler_args);
+ extra_compiler_args = NULL;
+ }
+
+ shell(&proc, SHELL_OUTPUT, cmd);
+ if (proc == NULL) {
+ return NULL;
+ }
+
+ if (proc->output != NULL) {
+ strip(proc->output);
+ if (isempty(proc->output) == 0) {
+ printf("%s\n", proc->output);
+ }
+ }
+ if (exit_code) {
+ *exit_code = proc->returncode;
+ }
+
+ return realpath(img_filename, NULL);
+}
+
#define myassert(condition, ...) \
do { \
if (!(condition)) { \
diff --git a/tests/test_rpath_rpath_get.c b/tests/test_rpath_rpath_get.c
new file mode 100644
index 0000000..c8e573b
--- /dev/null
+++ b/tests/test_rpath_rpath_get.c
@@ -0,0 +1,21 @@
+#include "spm.h"
+#include "framework.h"
+
+const char *testFmt = "returned '%s', expected '%s'\n";
+
+int main(int argc, char *argv[]) {
+ int retval_lib = 0;
+ int retval_bin = 0;
+ char *filename_lib = mock_image(AS_MOCK_LIB, "libwinning", (char *[]) {"-L/usr/lib", "-lz", NULL}, &retval_lib);
+ char *filename_bin = mock_image(AS_MOCK_BIN, "winning", (char *[]) {"-L.", "-lwinning", NULL}, &retval_bin);
+ char *rpath_lib = rpath_get(filename_lib);
+ char *rpath_bin = rpath_get(filename_bin);
+
+ myassert(retval_lib == 0, "mock library build failed with code: %d\n", retval_lib);
+ myassert(strcmp(dirname(filename_lib), rpath_lib) == 0, testFmt, dirname(filename_lib), rpath_lib);
+
+ myassert(retval_lib == 0, "mock executable build failed with code: %d\n", retval_bin);
+ myassert(strcmp(dirname(filename_bin), rpath_bin) == 0, testFmt, dirname(filename_bin), rpath_bin);
+
+ return 0;
+} \ No newline at end of file