diff options
-rw-r--r-- | .circleci/config.yml | 1 | ||||
-rwxr-xr-x | .circleci/init.sh | 1 | ||||
-rwxr-xr-x | .circleci/test_spm.sh | 30 | ||||
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | include/CMakeLists.txt | 1 | ||||
-rw-r--r-- | include/checksum.h | 1 | ||||
-rw-r--r-- | include/error_handler.h | 1 | ||||
-rw-r--r-- | include/rpath.h | 1 | ||||
-rw-r--r-- | include/shlib.h | 4 | ||||
-rw-r--r-- | include/spm.h | 21 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 5 | ||||
-rw-r--r-- | lib/error_handler.c | 1 | ||||
-rw-r--r-- | lib/install.c | 1 | ||||
-rw-r--r-- | lib/internal_cmd.c | 9 | ||||
-rw-r--r-- | lib/rpath.c | 120 | ||||
-rw-r--r-- | lib/shlib.c | 91 | ||||
-rw-r--r-- | scripts/CMakeLists.txt | 18 | ||||
-rw-r--r-- | scripts/realpath.c | 28 | ||||
-rwxr-xr-x | scripts/spmbuild | 74 | ||||
-rw-r--r-- | src/CMakeLists.txt | 5 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tests/framework.h | 112 | ||||
-rw-r--r-- | tests/test_rpath_rpath_get.c | 21 |
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 |