]> git.xonotic.org Git - xonotic/netradiant.git/commitdiff
bundle: store dll in lib subdirectory, bundle all of them at once 136/head
authorThomas Debesse <dev@illwieckz.net>
Wed, 19 Jun 2019 18:45:53 +0000 (20:45 +0200)
committerThomas Debesse <dev@illwieckz.net>
Wed, 19 Jun 2019 19:03:34 +0000 (21:03 +0200)
instead of finding and copying the dll each time a binary is produced,
the path of the produced binary is stored in a unique file (to avoid race
condition) and the target for that binary is added as a dependency to a new
target named `bundle` that does the finding and the copying once for all.

this avoids race condition while building targets in parallel, workaround
the inability of cmake to track a common list between all targets and to keep
it from configure to build step

this makes the bundle process more reliable and avoid things being copied
twice. Before that I've seen copy processes starting to copy the same file
even with the -n option telling to not copy if the file exists, then seeing
one of them failing because they both started when the file was not yet
copied but one finished the copy before the other one

this also makes the bundling faster by only doing things once for all

the dll are then moved to a lib/ subdirectory instead of the root
of the buld directory

each binary having dll to find and copy is built with a special resource file
embedding a manifest telling there is an assembly directory containing dll
named lib, then a manifest is writen for that assembly directory listing all
the dll it can find there telling windows to look for them there at binary
loading instead of the system directory and the place right to binary

this:

- helps to ensure the right dll is loaded even if another exists
  with the same name on the system
- reduce the mess by storing all dll in one dedicated directory
- avoids the double cmake configure run by removing the need to
  glob the build directory before installation

CMakeLists.txt
README.md
bundle/CMakeLists.txt [new file with mode: 0644]
include/lib.rc [new file with mode: 0644]
library-bundler
radiant/CMakeLists.txt

index 154a873dbbed798b098b6912455ae64754963dce..1a414aa15ca54d811fe04400a1c221d3914f1622 100644 (file)
@@ -213,30 +213,18 @@ if (BUILD_BINARIES)
 
     option(BUNDLE_LIBRARIES "Bundle libraries" ${BUNDLE_LIBRARIES_DEFAULT})
 
+    if (BUNDLE_LIBRARIES)
+        add_subdirectory(bundle)
+    endif ()
+
     macro (copy_dlls target)
-        if (BUNDLE_LIBRARIES AND WIN32)
+        if (BUNDLE_LIBRARIES)
             add_custom_command(TARGET ${target} POST_BUILD
                 COMMAND "${PROJECT_SOURCE_DIR}/library-bundler"
-                ARGS "windows" "$<TARGET_FILE:${target}>" "${PROJECT_BINARY_DIR}"
+                "register" "${PROJECT_BINARY_DIR}" "$<TARGET_FILE:${target}>"
                 VERBATIM
             )
-        endif ()
-    endmacro ()
-
-    macro (bundle_stuff target)
-        if (BUNDLE_LIBRARIES AND WIN32)
-            file(GLOB DLL_FILES ${PROJECT_BINARY_DIR}/*.dll)
-
-            install(FILES
-                ${DLL_FILES}
-                DESTINATION ${CMAKE_INSTALL_PREFIX}
-            )
-
-            install(DIRECTORY
-                ${PROJECT_BINARY_DIR}/lib
-                ${PROJECT_BINARY_DIR}/share
-                DESTINATION ${CMAKE_INSTALL_PREFIX}
-            )
+            add_dependencies(bundle ${target})
         endif ()
     endmacro ()
 endif ()
@@ -279,7 +267,11 @@ if (BUILD_BINARIES)
     endif ()
 
     macro (radiant_tool name)
-        add_executable(${name} ${ARGN})
+        if (BUNDLE_LIBRARIES AND WIN32)
+            add_executable(${name} ${ARGN} ${PROJECT_SOURCE_DIR}/include/lib.rc)
+        else ()
+            add_executable(${name} ${ARGN})
+        endif ()
 
         copy_dlls(${name})
 
index 83a7f6c788c540d64c6eae71324a673633a5d47c..8aa17a4288141efa61ba1e9cbe4fa609dd169a54 100644 (file)
--- a/README.md
+++ b/README.md
@@ -161,12 +161,6 @@ target:
 * `install`  
   Install files
 
-Note that because of both the way NetRadiant works and the way bundled library loading works CMake has to do some globbing to detect some of the produced/copied files it has to install. So you have to run cmake again before installing:
-
-```
-cmake -H. -Bbuild && cmake --build build -- install
-```
-
 ## Note about Crunch
 
 The crnlib used to decode `.crn` files is the one from [Dæmon](http://github.com/DaemonEngine/Daemon) which is the one by [Unity](https://github.com/Unity-Technologies/crunch/tree/unity) made cross-platform and slightly improved. Since Unity brokes compatibility with [BinomialLLC's legacy tree](https://github.com/BinomialLLC/crunch) it's required to use either the `crunch` tool from Dæmon or the one from Unity to compress textures that have to be read by radiant or q3map2.
diff --git a/bundle/CMakeLists.txt b/bundle/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1cb5a22
--- /dev/null
@@ -0,0 +1,18 @@
+if (WIN32)
+    set(BUNDLE_OS_NAME "windows")
+else ()
+    set(BUNDLE_OS_NAME "unsupported")
+endif ()
+
+add_custom_target(bundle ALL
+    COMMAND "${PROJECT_SOURCE_DIR}/library-bundler"
+    "${BUNDLE_OS_NAME}" "${PROJECT_BINARY_DIR}"
+    VERBATIM
+    COMMENT "Bundling libraries"
+)
+
+install(DIRECTORY
+    ${PROJECT_BINARY_DIR}/lib
+    ${PROJECT_BINARY_DIR}/share
+    DESTINATION ${CMAKE_INSTALL_PREFIX}
+)
diff --git a/include/lib.rc b/include/lib.rc
new file mode 100644 (file)
index 0000000..3ad3a12
--- /dev/null
@@ -0,0 +1,11 @@
+1 24 /* RT_MANIFEST */
+BEGIN
+    "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>"
+    "<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">"
+      "<dependency>"
+        "<dependentAssembly>"
+          "<assemblyIdentity type=""win32"" name=""lib"" version=""1.0.0.0""/>"
+        "</dependentAssembly>"
+      "</dependency>"
+    "</assembly>"
+END
index 7741e63e8e6ccce1780fd982e77302c4a4145452..c2855be6261c8b3661fc01b4bda953f56e1b0412 100644 (file)
 #! /usr/bin/env bash
 
-system_name="${1}"
-exe_file="${2}"
-bundle_dir="${3}"
+Windows::listLibForManifest () {
+       local lib_dir="${1}"
+
+       find "${lib_dir}" \
+               -maxdepth 1 \
+               -type f \
+               -name '*.dll' \
+               -exec basename {} \; \
+       | xargs -I {} \
+               printf '  <file name="{}"/>\n'
+}
+
+Windows::printManifest () {
+       local lib_dir="${1}"
+
+       cat <<-EOF
+       <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+         <assemblyIdentity type="win32" name="lib" version="1.0.0.0"/>
+       $(Windows::listLibForManifest "${lib_dir}")
+       </assembly>
+       EOF
+}
+
+Windows::bundleLibFromFile () {
+       local exe_file="${1}"
+
+       exe_file="$(cygpath --unix "${exe_file}")"
+
+       ntldd --recursive "${exe_file}" \
+       | egrep -i '\.dll => [A-Z]:\\msys64\\' \
+       | sed -e 's/ (0x[0-9a-f]*)$//;s/^.* => //' \
+       | cygpath --unix --file - \
+       | while read dll_file
+       do
+               dll_basename="$(basename "${dll_file}")"
+
+               if [ -f "${bundle_dir}/${dll_basename}" ]
+               then
+                       continue
+               fi
+
+               cp --preserve=timestamps "${dll_file}" "${lib_dir}/${dll_basename}"
+
+               if [ "${dll_basename}" = 'libgtk-win32-2.0-0.dll' ]
+               then
+                       mingw="$(which 'libgtk-win32-2.0-0.dll' | cut -f2 -d'/')"
+
+                       for component_dir in \
+                               'share/themes/MS-Windows' \
+                               'share/icons/hicolor' \
+                               'lib/gdk-pixbuf-2.0' \
+                               'lib/gtk-2.0'
+                       do
+                               if ! [ -d "${bundle_dir}/${component_dir}" ]
+                               then
+                                       mkdir --parents "${bundle_dir}/$(dirname "${component_dir}")"
+                                       cp -r --preserve=timestamps "/${mingw}/${component_dir}" \
+                                               "${bundle_dir}/${component_dir}"
+                               fi
+                       done
+
+                       find "${bundle_dir}/lib" -type f -name '*.a' -exec rm {} \;
+                       find "${bundle_dir}/lib" -type f -name '*.h' -exec rm {} \;
+                       find "${bundle_dir}/lib" -type d -exec rmdir --ignore-fail-on-non-empty {} \;
+               fi
+       done
+}
+
+system_name="${1}"; shift
+bundle_dir="${1}"; shift
+exe_file="${1}"; shift
+
+registry_dir="${bundle_dir}/registry"
 
 case "${system_name}" in
+       'register')
+               mkdir --parents "${registry_dir}"
+               printf '%s\n' "${exe_file}" > "${registry_dir}/$(uuidgen)"
+               ;;
        'windows')
                bundle_dir="$(cygpath --unix "${bundle_dir}")"
-               exe_file="$(cygpath --unix "${exe_file}")"
-               ntldd --recursive "${exe_file}" \
-               | egrep -i '\.dll => [A-Z]:\\msys64\\' \
-               | sed -e 's/ (0x[0-9a-f]*)$//;s/^.* => //' \
-               | cygpath --unix --file - \
-               | while read dll_file
-               do
-                       dll_basename="$(basename "${dll_file}")"
-
-                       if [ -f "${bundle_dir}/${dll_basename}" ]
-                       then
-                               continue
-                       fi
-
-                       cp --preserve=timestamps "${dll_file}" "${bundle_dir}/${dll_basename}"
-
-                       if [ "${dll_basename}" = 'libgtk-win32-2.0-0.dll' ]
-                       then
-                               mingw="$(which 'libgtk-win32-2.0-0.dll' | cut -f2 -d'/')"
-
-                               for component_dir in \
-                                       'share/themes/MS-Windows' \
-                                       'share/icons/hicolor' \
-                                       'lib/gdk-pixbuf-2.0' \
-                                       'lib/gtk-2.0'
-                               do
-                                       if ! [ -d "${bundle_dir}/${component_dir}" ]
-                                       then
-                                               mkdir --parents "${bundle_dir}/$(dirname "${component_dir}")"
-                                               cp -r --preserve=timestamps "/${mingw}/${component_dir}" \
-                                                       "${bundle_dir}/${component_dir}"
-                                       fi
-                               done
-
-                               find "${bundle_dir}/lib" -type f -name '*.a' -exec rm {} \;
-                               find "${bundle_dir}/lib" -type f -name '*.h' -exec rm {} \;
-                               find "${bundle_dir}/lib" -type d -exec rmdir --ignore-fail-on-non-empty {} \;
-                       fi
-               done
+               
+               lib_dir="${bundle_dir}/lib"
+               mkdir --parents "${lib_dir}"
+
+               if [ -d "${registry_dir}" ]
+               then
+                       for registry_entry in "${registry_dir}"/*
+                       do
+                               exe_file="$(cat "${registry_entry}")"
+
+                               Windows::bundleLibFromFile "${exe_file}"
+
+                               rm "${registry_entry}"
+                               rmdir --ignore-fail-on-non-empty "${registry_dir}"
+                       done
+                       
+                       manifest_file="${lib_dir}/lib.manifest"
+                       Windows::printManifest "${lib_dir}" > "${manifest_file}"
+               fi
                ;;
        *)
                printf 'ERROR: unsupported system: %s\n' "${system_name}" >&2
index 424c42d6675e97bab647222f69af668cc74bd129..6fcb36809d27fa167e98fa8a4661672d42f47e66 100644 (file)
@@ -131,4 +131,3 @@ if (X11_LIBRARIES)
 endif ()
 
 copy_dlls(${RADIANT_BASENAME})
-bundle_stuff(${RADIANT_BASENAME})