Просмотр исходного кода

BundleUtilities: Work w/ non .app exes on Mac (#12034)

Also add a test of BundleUtilities including an exe,
some shared libs, a plugin, and a framework-style lib.

This test presently runs (and this functionality works)
on Linux, Mac and Windows.

For now, the framework-style lib is built as a plain old
shared lib because there is another yet-unresolved issue
with local frameworks without rpaths on the Mac.
Clinton Stimpson 14 лет назад
Родитель
Сommit
7ac7b437b8

+ 25 - 26
Modules/BundleUtilities.cmake

@@ -53,9 +53,8 @@
 #
 #
 #  GET_DOTAPP_DIR(<exe> <dotapp_dir_var>)
 #  GET_DOTAPP_DIR(<exe> <dotapp_dir_var>)
 # Returns the nearest parent dir whose name ends with ".app" given the full
 # Returns the nearest parent dir whose name ends with ".app" given the full
-# path to an executable. If there is no such parent dir, then return a dir at
-# the same level as the executable, named with the executable's base name and
-# ending with ".app"
+# path to an executable. If there is no such parent dir, then simply return
+# the dir containing the executable.
 #
 #
 # The returned directory may or may not exist.
 # The returned directory may or may not exist.
 #
 #
@@ -227,35 +226,35 @@ endfunction(get_bundle_main_executable)
 function(get_dotapp_dir exe dotapp_dir_var)
 function(get_dotapp_dir exe dotapp_dir_var)
   set(s "${exe}")
   set(s "${exe}")
 
 
-  set(has_dotapp_parent 0)
   if(s MATCHES "^.*/.*\\.app/.*$")
   if(s MATCHES "^.*/.*\\.app/.*$")
-    set(has_dotapp_parent 1)
-  endif(s MATCHES "^.*/.*\\.app/.*$")
-
-  set(done 0)
-  while(NOT ${done})
-    get_filename_component(snamewe "${s}" NAME_WE)
-    get_filename_component(sname "${s}" NAME)
-    get_filename_component(sdir "${s}" PATH)
-    if(has_dotapp_parent)
-      # If there is a ".app" parent directory,
-      # ascend until we hit it:
-      #   (typical of a Mac bundle executable)
-      #
+    # If there is a ".app" parent directory,
+    # ascend until we hit it:
+    #   (typical of a Mac bundle executable)
+    #
+    set(done 0)
+    while(NOT ${done})
+      get_filename_component(snamewe "${s}" NAME_WE)
+      get_filename_component(sname "${s}" NAME)
+      get_filename_component(sdir "${s}" PATH)
       set(s "${sdir}")
       set(s "${sdir}")
       if(sname MATCHES "\\.app$")
       if(sname MATCHES "\\.app$")
         set(done 1)
         set(done 1)
         set(dotapp_dir "${sdir}/${sname}")
         set(dotapp_dir "${sdir}/${sname}")
       endif(sname MATCHES "\\.app$")
       endif(sname MATCHES "\\.app$")
-    else(has_dotapp_parent)
-      # Otherwise use a directory named the same
-      # as the exe, but with a ".app" extension:
-      #   (typical of a non-bundle executable on Mac, Windows or Linux)
-      #
-      set(done 1)
-      set(dotapp_dir "${sdir}/${snamewe}.app")
-    endif(has_dotapp_parent)
-  endwhile(NOT ${done})
+    endwhile(NOT ${done})
+  else(s MATCHES "^.*/.*\\.app/.*$")
+    # Otherwise use a directory containing the exe
+    #   (typical of a non-bundle executable on Mac, Windows or Linux)
+    #
+    is_file_executable("${s}" is_executable)
+    if(is_executable)
+      get_filename_component(sdir "${s}" PATH)
+      set(dotapp_dir "${sdir}")
+    else(is_executable)
+      set(dotapp_dir "${s}")
+    endif(is_executable)
+  endif(s MATCHES "^.*/.*\\.app/.*$")
+
 
 
   set(${dotapp_dir_var} "${dotapp_dir}" PARENT_SCOPE)
   set(${dotapp_dir_var} "${dotapp_dir}" PARENT_SCOPE)
 endfunction(get_dotapp_dir)
 endfunction(get_dotapp_dir)

+ 2 - 2
Modules/GetPrerequisites.cmake

@@ -261,9 +261,9 @@ function(gp_resolve_item context item exepath dirs resolved_item_var)
 
 
   # Is it already resolved?
   # Is it already resolved?
   #
   #
-  if(EXISTS "${resolved_item}")
+  if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
     set(resolved 1)
     set(resolved 1)
-  endif(EXISTS "${resolved_item}")
+  endif(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
 
 
   if(NOT resolved)
   if(NOT resolved)
     if(item MATCHES "@executable_path")
     if(item MATCHES "@executable_path")

+ 70 - 0
Tests/BundleUtilities/CMakeLists.txt

@@ -0,0 +1,70 @@
+cmake_minimum_required(VERSION 2.8)
+project(BundleUtilities)
+
+###### the various types of dependencies we can have
+
+# a shared library
+add_library(shared SHARED shared.cpp shared.h)
+
+# another shared library
+add_library(shared2 SHARED shared2.cpp shared2.h)
+
+# a loadable module (depends on shared2)
+# test app will load this at runtime
+add_library(module MODULE module.cpp module.h)
+set_target_properties(module PROPERTIES PREFIX "")
+get_target_property(module_loc module LOCATION)
+target_link_libraries(module shared2)
+
+# a framework library
+add_library(framework SHARED framework.cpp framework.h)
+# TODO: fix problems with local frameworks without rpaths
+#set_target_properties(framework PROPERTIES FRAMEWORK 1)
+
+# make sure rpaths are not helping BundleUtilities or the executables
+set_target_properties(shared shared2 module framework PROPERTIES
+                      SKIP_BUILD_RPATH 1)
+
+
+######  test a Bundle application using dependencies
+
+set(TESTBUNDLEDIR "${CMAKE_CURRENT_BINARY_DIR}/testdir1")
+add_executable(testbundleutils1 MACOSX_BUNDLE testbundleutils.cpp)
+target_link_libraries(testbundleutils1 shared framework ${CMAKE_DL_LIBS})
+set_target_properties(testbundleutils1 PROPERTIES
+                      INSTALL_RPATH "${TESTBUNDLEDIR}"
+                      BUILD_WITH_INSTALL_RPATH 1)
+get_target_property(loc testbundleutils1 LOCATION)
+
+add_custom_target(testbundleutils1_test  ALL
+  COMMAND ${CMAKE_COMMAND}
+  "-DINPUT=${loc}"
+  "-DMODULE=${module_loc}"
+  "-DINPUTDIR=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
+  "-DOUTPUTDIR=${TESTBUNDLEDIR}"
+  -P "${CMAKE_CURRENT_SOURCE_DIR}/bundleutils.cmake"
+  )
+
+add_dependencies(testbundleutils1_test testbundleutils1)
+
+
+
+######  test a non-Bundle application using dependencies
+
+set(TESTBUNDLEDIR "${CMAKE_CURRENT_BINARY_DIR}/testdir2")
+add_executable(testbundleutils2 testbundleutils.cpp)
+target_link_libraries(testbundleutils2 shared framework ${CMAKE_DL_LIBS})
+set_target_properties(testbundleutils2 PROPERTIES
+                      INSTALL_RPATH "${TESTBUNDLEDIR}"
+                      BUILD_WITH_INSTALL_RPATH 1)
+get_target_property(loc testbundleutils2 LOCATION)
+
+add_custom_target(testbundleutils2_test  ALL
+  COMMAND ${CMAKE_COMMAND}
+  "-DINPUT=${loc}"
+  "-DMODULE=${module_loc}"
+  "-DINPUTDIR=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
+  "-DOUTPUTDIR=${TESTBUNDLEDIR}"
+  -P "${CMAKE_CURRENT_SOURCE_DIR}/bundleutils.cmake"
+  )
+add_dependencies(testbundleutils2_test testbundleutils2)

+ 44 - 0
Tests/BundleUtilities/bundleutils.cmake

@@ -0,0 +1,44 @@
+
+# clean passed in arguments
+get_filename_component(INPUT ${INPUT} ABSOLUTE)
+get_filename_component(INPUTDIR ${INPUTDIR} ABSOLUTE)
+
+message("INPUT = ${INPUT}")
+message("MODULE = ${MODULE}")
+message("INPUTDIR = ${INPUTDIR}")
+message("OUTPUTDIR = ${OUTPUTDIR}")
+
+# compute location to install/test things
+file(RELATIVE_PATH relative_exe "${INPUTDIR}" "${INPUT}")
+set(OUTPUT "${OUTPUTDIR}/${relative_exe}")
+message("OUTPUT = ${OUTPUT}")
+get_filename_component(EXE_DIR "${OUTPUT}" PATH)
+get_filename_component(MODULE_NAME "${MODULE}" NAME)
+set(OUTPUT_MODULE "${EXE_DIR}/${MODULE_NAME}")
+message("OUTPUTMODULE = ${OUTPUT_MODULE}")
+
+# clean output dir
+file(REMOVE_RECURSE "${OUTPUTDIR}")
+# copy the app and plugin to installation/testing directory
+configure_file("${INPUT}" "${OUTPUT}" COPYONLY)
+configure_file("${MODULE}" "${OUTPUT_MODULE}" COPYONLY)
+
+# have BundleUtilities grab all dependencies and
+# check that the app runs
+
+# for this test we'll override location to put all dependencies
+# (in the same dir as the app)
+# this shouldn't be necessary except for the non-bundle case on Mac
+function(gp_item_default_embedded_path_override item path)
+  set(path "@executable_path" PARENT_SCOPE)
+endfunction(gp_item_default_embedded_path_override)
+
+include(BundleUtilities)
+fixup_bundle("${OUTPUT}" "${OUTPUT_MODULE}" "${INPUTDIR}")
+
+# make sure we can run the app
+execute_process(COMMAND "${OUTPUT}" RESULT_VARIABLE result)
+
+if(NOT result STREQUAL "0")
+  message(FATAL_ERROR " failed to execute test program")
+endif(NOT result STREQUAL "0")

+ 8 - 0
Tests/BundleUtilities/framework.cpp

@@ -0,0 +1,8 @@
+
+#include "framework.h"
+#include "stdio.h"
+
+void framework()
+{
+  printf("framework\n");
+}

+ 17 - 0
Tests/BundleUtilities/framework.h

@@ -0,0 +1,17 @@
+
+#ifndef framework_h
+#define framework_h
+
+#ifdef WIN32
+# ifdef framework_EXPORTS
+#  define FRAMEWORK_EXPORT __declspec(dllexport)
+# else
+#  define FRAMEWORK_EXPORT __declspec(dllimport)
+# endif
+#else
+# define FRAMEWORK_EXPORT
+#endif
+
+void FRAMEWORK_EXPORT framework();
+
+#endif

+ 10 - 0
Tests/BundleUtilities/module.cpp

@@ -0,0 +1,10 @@
+
+#include "module.h"
+#include "stdio.h"
+#include "shared2.h"
+
+void module()
+{
+  printf("module\n");
+  shared2();
+}

+ 7 - 0
Tests/BundleUtilities/module.h

@@ -0,0 +1,7 @@
+
+#ifndef module_h
+#define module_h
+
+void module();
+
+#endif

+ 8 - 0
Tests/BundleUtilities/shared.cpp

@@ -0,0 +1,8 @@
+
+#include "shared.h"
+#include "stdio.h"
+
+void shared()
+{
+  printf("shared\n");
+}

+ 17 - 0
Tests/BundleUtilities/shared.h

@@ -0,0 +1,17 @@
+
+#ifndef shared_h
+#define shared_h
+
+#ifdef WIN32
+# ifdef shared_EXPORTS
+#  define SHARED_EXPORT __declspec(dllexport)
+# else
+#  define SHARED_EXPORT __declspec(dllimport)
+# endif
+#else
+# define SHARED_EXPORT
+#endif
+
+void SHARED_EXPORT shared();
+
+#endif

+ 8 - 0
Tests/BundleUtilities/shared2.cpp

@@ -0,0 +1,8 @@
+
+#include "shared2.h"
+#include "stdio.h"
+
+void shared2()
+{
+  printf("shared2\n");
+}

+ 17 - 0
Tests/BundleUtilities/shared2.h

@@ -0,0 +1,17 @@
+
+#ifndef shared2_h
+#define shared2_h
+
+#ifdef WIN32
+# ifdef shared2_EXPORTS
+#  define SHARED2_EXPORT __declspec(dllexport)
+# else
+#  define SHARED2_EXPORT __declspec(dllimport)
+# endif
+#else
+# define SHARED2_EXPORT
+#endif
+
+void SHARED2_EXPORT shared2();
+
+#endif

+ 23 - 0
Tests/BundleUtilities/testbundleutils.cpp

@@ -0,0 +1,23 @@
+
+#include "framework.h"
+#include "shared.h"
+
+#if defined(WIN32)
+#include <windows.h>
+#else
+#include "dlfcn.h"
+#endif
+
+int main(int, char**)
+{
+  framework();
+  shared();
+
+#if defined(WIN32)
+  HANDLE lib = LoadLibraryA("module.dll");
+#else
+  void* lib = dlopen("module.so", RTLD_LAZY);
+#endif
+
+  return lib == 0 ? 1 : 0;
+}

+ 19 - 0
Tests/CMakeLists.txt

@@ -193,6 +193,25 @@ IF(BUILD_TESTING)
 
 
   LIST(APPEND TEST_BUILD_DIRS ${CMake_TEST_INSTALL_PREFIX})
   LIST(APPEND TEST_BUILD_DIRS ${CMake_TEST_INSTALL_PREFIX})
 
 
+
+  # run test for BundleUtilities on supported platforms
+  if(CMAKE_SYSTEM_NAME MATCHES "Windows" OR
+     CMAKE_SYSTEM_NAME MATCHES "Linux" OR
+     CMAKE_SYSTEM_NAME MATCHES "Darwin")
+    ADD_TEST(BundleUtilities ${CMAKE_CTEST_COMMAND}
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/BundleUtilities"
+      "${CMake_BINARY_DIR}/Tests/BundleUtilities"
+      --build-generator ${CMAKE_TEST_GENERATOR}
+      --build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
+      --build-project BundleUtilities
+      --test-command testdir2/testbundleutils2
+      )
+    LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/BundleUtilities")
+  endif(CMAKE_SYSTEM_NAME MATCHES "Windows" OR
+     CMAKE_SYSTEM_NAME MATCHES "Linux" OR
+     CMAKE_SYSTEM_NAME MATCHES "Darwin")
+
   SET(CMAKE_BUILD_TEST_SOURCE_DIR "${CMake_SOURCE_DIR}/Tests/COnly")
   SET(CMAKE_BUILD_TEST_SOURCE_DIR "${CMake_SOURCE_DIR}/Tests/COnly")
   SET(CMAKE_BUILD_TEST_BINARY_DIR "${CMake_BINARY_DIR}/Tests/CMakeBuildCOnly")
   SET(CMAKE_BUILD_TEST_BINARY_DIR "${CMake_BINARY_DIR}/Tests/CMakeBuildCOnly")
   CONFIGURE_FILE("${CMake_SOURCE_DIR}/Tests/CMakeBuildTest.cmake.in"
   CONFIGURE_FILE("${CMake_SOURCE_DIR}/Tests/CMakeBuildTest.cmake.in"