1#!/bin/bash
2
3make_boolean() {
4  OPTION="${1}"
5
6  case "${OPTION}" in
7    ("0"|"no"|""|"No"|"nO"|"NO"|"false"|"FALSE") OPTION="0";;
8    (*) OPTION="1";;
9  esac
10
11  printf "${OPTION}"
12}
13
14phase() {
15  echo
16  echo "***"
17  echo "*** ${1}..."
18  echo "***"
19  echo
20}
21
22usage() {
23  exec >&2
24
25  NO_VAL="0, no, NO, No, nO, false or FALSE"
26  printf "$(basename ${0}): usage\n\n"
27  printf "Accepted environment variables:\n"
28  printf "\tSDK:\t\t\t\tsets the target SDK [string]\n\t\t\t\t\tdefault: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk\n"
29  printf "\tMACOSX_DEPLOYMENT_TARGET:\tsets the deployment target (specific OS X version to optimize/build for) [string]\n\t\t\t\t\tdefault: 10.7\n"
30  printf "\tSTDLIB:\t\t\t\tsets a specific stdlib variant. Must be used with FORCE_STDLIB to have any effect. [string]\n\t\t\t\t\tdefault: autodetect\n"
31  printf "\tFORCE_STDLIB:\t\t\tforces a specific C++ stdlib version. If you use this, also specify STDLIB. YOU SHOULD NEVER USE THIS, UNLESS YOU KNOW WHAT YOU ARE DOING! [boolean]\n\t\t\t\t\tdefault: disabled\n"
32  printf "\tDEBUG\t\t\t\tenables or disables debug builds [boolean]\n\t\t\t\t\tdefault: disabled\n"
33  printf "\tBUNDLE\t\t\t\tenables or disables library bundling and the creation of a .dmg installer [boolean]\n\t\t\t\t\tdefault: enabled\n"
34  printf "\tUNIVERSAL\t\t\tenables or disables x86 support. x86_64 support is always enabled [boolean]\n\t\t\t\t\tdefault: enabled\n"
35  printf "\tMACPORTS_PREFIX\t\t\tsets the (MacPorts) prefix used to detect PulseAudio, nxproxy and xauth binaries [string]\n\t\t\t\t\tdefault: /opt/local/\n"
36  printf "\n"
37  printf "Boolean values help:\n"
38  printf "\ta value of ${NO_VAL} will be treated as false\n"
39  printf "\tany other value will be treated as true\n"
40
41  exit 2
42}
43
44dependency_error() {
45  exec >&2
46
47  typeset element="${1}"; shift
48  typeset component="${1}"; shift
49  typeset type="${1}"; shift
50
51  echo "${element} ${type} not found."
52  echo "Install ${component} -- e.g., via "port -vt install ${component}" if using MacPorts."
53  echo "If ${component} is already installed, try passing MACPORTS_PREFIX if the autodetected or default value (${MACPORTS_PREFIX}) does not match your setup."
54
55  exit 3
56}
57
58lazy_canonical_path() {
59  typeset path="${1}"
60
61  typeset old_path=""
62  while [ "${old_path}" != "${path}" ]; do
63    old_path="${path}"
64    path="${path//\/\///}"
65  done
66
67  printf "${old_path}"
68}
69
70get_nesting_level() {
71set -x
72  typeset -i level=0
73  typeset path="${1}"
74
75  while [ -n "${path}" ] && [ "${path}" != "." ] && [ "${path}" != "/" ]; do
76    i="$((${i} + 1))"
77    path="$(dirname "${path}")"
78  done
79
80  printf "${level}"
81set +x
82}
83
84repeat_str() { # INPUT COUNT
85  typeset INPUT="${1:?"Error: no input string passed to ${FUNCNAME}()."}"
86  typeset COUNT="${2:?"Error: no count passed to ${FUNCNAME}()."}"
87
88  typeset ret=""
89  typeset -i i=0
90  while [ "${i}" -lt "${COUNT}" ]; do
91    ret="${ret}$(printf "${INPUT}")"
92    i=$((${i} + 1))
93  done
94
95  printf "${ret}"
96
97  return 0
98}
99
100typeset -a otool_fail_str
101otool_fail_str=( "is not an object file"
102     "can't open file"
103     "Archive : " )
104
105parse_otool_output() {
106#set -x
107  typeset raw_output="${@}"
108
109  typeset fail_str=""
110  for fail_str in "${otool_fail_str[@]}"; do
111    if echo "${raw_output}" | grep -q "${fail_str}"; then
112      return 1
113    fi
114  done
115
116  typeset tmp_regex='^[[:space:]]+(.*)[[:space:]]\(compatibility version .*, current version .*\)'
117
118  # In this special case, we do not want read to perform any word splitting.
119  typeset oldifs="${IFS}"
120  IFS=''
121
122  # Used for skipping the ID entry.
123  # Initialized to the empty string, but the first matching line will set it once.
124  # The ID filename is required for subsequent dependency discovery.
125  typeset id=""
126
127  typeset line=""
128  while read -r line; do
129    if [[ "${line}" =~ ${tmp_regex} ]]; then
130      typeset file="${BASH_REMATCH[1]}"
131
132      if [ -z "${id}" ]; then
133        echo "ID unset, something is wrong" >&2
134        return 1
135      elif [ "$(basename "${file}")" != "${id}" ]; then
136        echo "${BASH_REMATCH[1]}"
137      else
138        first="0"
139      fi
140    elif [ -z "${id}" ]; then
141      id="$(basename "${line%":"}")"
142    fi
143  done <<< "${raw_output}"
144
145  IFS="${oldifs}"
146#set +x
147  return 0
148}
149
150MATCH_HELP='(^((-h)|(--help))([ 	]|$))|([ 	]+((-h)|(--help))([ 	]|$))'
151[ -n "${*}" ] && [[ "${*}" =~ ${MATCH_HELP} ]] && usage
152
153NAME="x2goclient"
154
155TOP_DIR="$(dirname "$0")"
156[[ "${TOP_DIR}" == /* ]] || TOP_DIR="${PWD}/${TOP_DIR#./}"
157BUILD_DIR="${TOP_DIR}/client_build"
158APPBUNDLE="${BUILD_DIR}/${NAME}.app"
159EXE_DIR="${APPBUNDLE}/Contents/exe/"
160FRAMEWORKS_DIR="${APPBUNDLE}/Contents/Frameworks/"
161RESOURCES_DIR="${APPBUNDLE}/Contents/Resources/"
162DMGFILE="${BUILD_DIR}/${NAME}.dmg"
163PROJECT="${TOP_DIR}/${NAME}.pro"
164PKG_DMG="${TOP_DIR}/pkg-dmg"
165
166# Try to find the MacPorts prefix.
167typeset MACPORTS_PREFIX_SEARCH=""
168if type -P port >/dev/null 2>&1; then
169  MACPORTS_PREFIX_SEARCH="$(type -P port)"
170  MACPORTS_PREFIX_SEARCH="${MACPORTS_PREFIX_SEARCH%%bin/port}"
171else
172  # Port not being found in ${PATH} doesn't necessarily mean it isn't available.
173  # Try to guess.
174  MACPORTS_PREFIX_SEARCH="/opt/local/"
175fi
176
177NXPROXY="nxproxy"
178NXAUTH="xauth"
179PULSEAUDIO_BINARIES=( "pulseaudio" "esdcompat" "pacat" "pacmd"      "pactl"
180                      "pamon"      "paplay"    "parec" "parecord"   "pasuspender" )
181PULSEAUDIO_LIBRARIES=( "libpulse-simple.0.dylib"
182                       "pulse-13.0"
183                       "pulseaudio" )
184RESOURCE_FILES=( "audio/startup.wav" )
185
186typeset -a special_files_regex
187special_files_regex+=( "pulseaudio/libpulsecommon-[0-9]+\.[0-9]+\.dylib"
188                       "pulseaudio/libpulsecore-[0-9]+\.[0-9]+\.dylib"   )
189
190typeset -r dependency_base_format='@executable_path/../Frameworks/'
191
192: ${SDK:="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk"}
193: ${MACOSX_DEPLOYMENT_TARGET:="10.7"}
194: ${FORCE_STDLIB:="0"}
195: ${DEBUG:="0"}
196: ${BUNDLE:="1"}
197: ${UNIVERSAL:="1"}
198: ${MACPORTS_PREFIX:="${MACPORTS_PREFIX_SEARCH}"}
199
200unset MACPORTS_PREFIX_SEARCH
201
202FORCE_STDLIB="$(make_boolean "${FORCE_STDLIB}")"
203DEBUG="$(make_boolean "${DEBUG}")"
204BUNDLE="$(make_boolean "${BUNDLE}")"
205UNIVERSAL="$(make_boolean "${UNIVERSAL}")"
206
207[ "${DEBUG}" -eq "0" ] && BUILD_MODE="release" || BUILD_MODE="debug"
208
209BUILD_ARCH="x86_64"
210[ "${UNIVERSAL}" -eq "1" ] && BUILD_ARCH="${BUILD_ARCH} x86"
211
212if [ "${FORCE_STDLIB}" -eq "1" ]; then
213  if [[ -z "${STDLIB}" ]]; then
214    echo "stdlib forcing enabled, but STDLIB not passed." >&2
215    exit 1
216  fi
217
218  if [[ "${STDLIB}" != "libc++" && "${STDLIB}" != "libstdc++" ]]; then
219    echo "stdlib forcing enabled, but STDLIB contains illegal value. Legal values: libc++, libstdc++" >&2
220    exit 1
221  fi
222else
223  SDK_MINOR_VERSION="$(/usr/bin/perl -pe 's#.*?10\.(\d+).*?\.sdk$#\1#' <<< "${SDK}")"
224  MATCH_NUMBERS='^[0-9]+$'
225  if [[ "${SDK_MINOR_VERSION}" =~ ${MATCH_NUMBERS} ]]; then
226    [ "${SDK_MINOR_VERSION}" -gt "6" ] && STDLIB="libstdc++"
227    [ "${SDK_MINOR_VERSION}" -gt "8" ] && STDLIB="libc++"
228  else
229    echo "Unable to determine OS X version. Unknown value '${SDK_MINOR_VERSION}'." >&2
230    exit 1
231  fi
232fi
233
234# Gather files.
235NXPROXY="$(lazy_canonical_path "${MACPORTS_PREFIX}/bin/${NXPROXY}")"
236NXAUTH="$(lazy_canonical_path "${MACPORTS_PREFIX}/bin/${NXAUTH}")"
237
238[ -x "${NXPROXY}" ] || dependency_error "nxproxy" "nxproxy" "binary"
239[ -x "${NXAUTH}" ] || dependency_error "xauth" "xauth" "binary"
240
241typeset -i i
242typeset -i fail
243typeset -a PULSEAUDIO_BINARIES_FULL
244typeset cur_binary
245fail="0"
246for cur_binary in ${PULSEAUDIO_BINARIES[@]}; do
247  cur_binary="$(lazy_canonical_path "${MACPORTS_PREFIX}/bin/${cur_binary}")"
248
249  if [ -x "${cur_binary}" ]; then
250    PULSEAUDIO_BINARIES_FULL+=( "${cur_binary}" )
251  else
252    fail="1"
253    break
254  fi
255done
256
257[ "${fail}" -eq "1" ] && dependency_error "${cur_binary##"$(lazy_canonical_path "${MACPORTS_PREFIX}/bin/")"}" "pulseaudio" "binary"
258
259typeset cur_lib_or_libdir
260typeset -a PULSEAUDIO_LIBRARIES_FULL
261fail="0"
262for cur_lib_or_libdir in ${PULSEAUDIO_LIBRARIES[@]}; do
263  cur_lib_or_libdir="$(lazy_canonical_path "${MACPORTS_PREFIX}/lib/${cur_lib_or_libdir}")"
264
265  if [ ! -d "${cur_lib_or_libdir}" ] && [ -x "${cur_lib_or_libdir}" ]; then
266    echo "Adding ${cur_lib_or_libdir} to \${PULSEAUDIO_LIBRARIES_FULL}"
267    PULSEAUDIO_LIBRARIES_FULL+=( "${cur_lib_or_libdir}" )
268  elif [ -d "${cur_lib_or_libdir}" ]; then
269    # That's a directory... more work needed here.
270    echo "Scrubbing directory ${cur_lib_or_libdir}"
271    typeset entry=""
272
273    # -r parameter to read: Backslashes may NOT escape any characters!
274    # -d '': specifies the delimiter to be used - as '' resolves to an empty string followed
275    #        by a NUL character, the delimiter is set to this very NUL (\000) character.
276    while read -r -d '' entry; do
277      typeset cur_file="$(basename "${entry}")"
278      typeset TMP_REGEX='^.*\.(\.[0-9]+){0,2}(so|dylib|bundle)$'
279
280      # This is only here should the PA build system ever break and create
281      # "linux-style" library file names. Let's hope it never actually comes to that.
282      typeset TMP_REGEX_LINUX_COMPAT='^.*\.(so|dylib|bundle)(\.[0-9]+){0,2}$'
283
284      if [[ "${cur_file}" =~ ${TMP_REGEX} ]] || [[ "${cur_file}" =~ ${TMP_REGEX_LINUX_COMPAT} ]]; then
285        # Filename matched the expected template.
286        echo "Adding $(lazy_canonical_path "${entry}") to \${PULSEAUDIO_LIBRARIES_FULL}"
287        PULSEAUDIO_LIBRARIES_FULL+=( "$(lazy_canonical_path "${entry}")" )
288      fi
289    done < <(gfind "${cur_lib_or_libdir}" -type 'f' -print0)
290  else
291    fail="1"
292    break
293  fi
294done
295
296[ "${fail}" -eq "1" ] && dependency_error "${cur_lib_or_libdir}" "pulseaudio" "library or library directory"
297
298set -e
299
300phase "Cleaning"
301make clean
302
303# Create gitlog.
304git --no-pager log --since "2 years ago" --format="%ai %aN (%h) %n%n%x09*%w(68,0,10) %s%d%n" > "ChangeLog.gitlog"
305mv "ChangeLog.gitlog" "res/txt/git-info"
306
307# Copy debian changelog as the general changelog.
308cp -a "debian/changelog" "res/txt/"
309
310[ -e "${BUILD_DIR}" ] && rm -rf "${BUILD_DIR}"
311
312mkdir "${BUILD_DIR}"
313pushd "${BUILD_DIR}"
314
315phase "Running lrelease"
316lrelease "${PROJECT}"
317
318phase "Running qmake"
319qmake -config "${BUILD_MODE}" -spec macx-g++ "${PROJECT}" \
320      CONFIG+="${BUILD_ARCH}" \
321      QMAKE_MAC_SDK="${SDK}" \
322      QMAKE_MACOSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
323      OSX_STDLIB="${STDLIB}" \
324      MACPORTS_INCLUDE_PATH="${MACPORTS_PREFIX}/include" \
325      MACPORTS_LIBRARY_PATH="${MACPORTS_PREFIX}/lib" \
326      MACPORTS_PREFIX="${MACPORTS_PREFIX}"
327
328phase "Running make"
329make -j2
330
331mkdir -p "${EXE_DIR}/"
332mkdir -p "${FRAMEWORKS_DIR}/"
333
334phase "Copying nxproxy"
335cp -av "${NXPROXY}" "${EXE_DIR}/"
336
337phase "Copying (n)xauth"
338cp -av "${NXAUTH}" "${EXE_DIR}/nxauth"
339
340phase "Copying misc resources"
341typeset cur_res_file
342for cur_res_file in ${RESOURCE_FILES[@]}; do
343  cp -av "${TOP_DIR}/res/${cur_res_file}" "${RESOURCES_DIR}/"
344done
345
346phase "Copying PulseAudio"
347for cur_binary in ${PULSEAUDIO_BINARIES_FULL[@]}; do
348  cp -av "${cur_binary}" "${EXE_DIR}/"
349done
350
351typeset intermediate_lib_dir=""
352for cur_binary in ${PULSEAUDIO_LIBRARIES_FULL[@]}; do
353set -x
354  intermediate_lib_dir="$(lazy_canonical_path "$(dirname "${cur_binary}")/")"
355  intermediate_lib_dir="${intermediate_lib_dir##"$(lazy_canonical_path "${MACPORTS_PREFIX}/lib/")"}"
356
357  mkdir -p "${FRAMEWORKS_DIR}/${intermediate_lib_dir}/"
358
359  cp -av "${cur_binary}" "${FRAMEWORKS_DIR}/${intermediate_lib_dir}/"
360set +x
361done
362
363if [ "${BUNDLE}" = "1" ]; then
364  phase "Bundling nxproxy"
365  dylibbundler \
366    --fix-file "${EXE_DIR}/nxproxy" \
367    --bundle-deps \
368    --dest-dir "${FRAMEWORKS_DIR}/" \
369    --install-path "@executable_path/../Frameworks/" \
370    --create-dir
371
372  phase "Bundling (n)xauth"
373  dylibbundler \
374    --fix-file "${EXE_DIR}/nxauth" \
375    --bundle-deps \
376    --dest-dir "${FRAMEWORKS_DIR}/" \
377    --install-path "@executable_path/../Frameworks/" \
378    --create-dir
379
380  phase "Bundling PulseAudio"
381  typeset cur_binary_name=""
382  for cur_binary in ${PULSEAUDIO_BINARIES_FULL[@]}; do
383    if [ ! -L "${cur_binary}" ]; then
384      cur_binary_name="$(basename "${cur_binary}")"
385      dylibbundler \
386        --fix-file "${EXE_DIR}/${cur_binary_name}" \
387        --bundle-deps \
388        --dest-dir "${FRAMEWORKS_DIR}/" \
389        --install-path "@executable_path/../Frameworks/" \
390        --create-dir \
391        --overwrite-files
392    fi
393  done
394
395  typeset intermediate_lib_dir=""
396  for cur_binary in ${PULSEAUDIO_LIBRARIES_FULL[@]}; do
397    intermediate_lib_dir="$(lazy_canonical_path "$(dirname "${cur_binary}")/")"
398    intermediate_lib_dir="${intermediate_lib_dir##"$(lazy_canonical_path "${MACPORTS_PREFIX}/lib/")"}"
399
400    if [ ! -L "${cur_binary}" ]; then
401      cur_binary_name="$(basename "${cur_binary}")"
402      echo "Handling ${cur_binary} => ${intermediate_lib_dir}/${cur_binary_name}."
403
404      typeset nesting_level="$(get_nesting_level "${intermediate_lib_dir}")"
405
406      dylibbundler \
407        --fix-file "${FRAMEWORKS_DIR}/${intermediate_lib_dir}/${cur_binary_name}" \
408        --bundle-deps \
409        --dest-dir "${FRAMEWORKS_DIR}/${intermediate_lib_dir}" \
410        --install-path "@executable_path/$(repeat_str "../" "${nesting_level}")../Frameworks/${intermediate_lib_dir}" \
411        --create-dir \
412        --overwrite-files
413    fi
414  done
415
416  phase "Deduplicating PulseAudio libraries and dependencies"
417  typeset -r base_dir="$(lazy_canonical_path "${FRAMEWORKS_DIR}")"
418
419  typeset -a all_files
420  typeset entry=""
421  while read -r -d '' entry; do
422    typeset sanitized_entry="$(lazy_canonical_path "${entry}")"
423    echo "Adding ${sanitized_entry} to all files"
424    all_files+=( "${sanitized_entry}" )
425  done < <(gfind "${base_dir}" -type 'f' -print0)
426
427  typeset -a top_files
428  for entry in "${all_files[@]}"; do
429    typeset relative_path="${entry##"$(lazy_canonical_path "${base_dir}/")"}"
430    typeset tmp_regex='^[^/]+$'
431    echo "Checking ${relative_path} against regex '${tmp_regex}'"
432    if [[ "${relative_path}" =~ ${tmp_regex} ]]; then
433      echo "${relative_path} is top file, adding to array."
434      top_files+=( "${relative_path}" )
435    fi
436  done
437
438  typeset -a duplicates
439  for entry in "${all_files[@]}"; do
440    typeset relative_path="${entry##"$(lazy_canonical_path "${base_dir}/")"}"
441    typeset file_name="$(basename "${entry}")"
442    typeset top_entry=""
443    for top_entry in "${top_files[@]}"; do
444      if [ "${top_entry}" != "${relative_path}" ]; then
445        if [ "${file_name}" = "${top_entry}" ]; then
446          echo "Adding duplicate: ${relative_path}"
447          duplicates+=( "${relative_path}" )
448        fi
449      fi
450    done
451  done
452
453  echo "duplicates array before:"
454  for entry in "${duplicates[@]}"; do
455    echo "${entry}"
456  done
457
458  typeset -i i="0"
459  for i in "${!duplicates[@]}"; do
460    entry="${duplicates[${i}]}"
461    typeset special_file_regex=""
462    for special_file_regex in "${special_files_regex[@]}"; do
463      typeset tmp_regex='^'"${special_file_regex}"'$'
464      if [[ "${entry}" =~ ${tmp_regex} ]]; then
465        cp -v "${base_dir}/$(basename "${entry}")" "${base_dir}/$(dirname "${special_file_regex}")/"
466        duplicates[${i}]="$(basename "${entry}")"
467        echo "Renamed ${entry} in duplicates array to ${duplicates[${i}]}"
468      fi
469    done
470  done
471
472  echo "duplicates array after:"
473  for entry in "${duplicates[@]}"; do
474    echo "${entry}"
475  done
476
477  for entry in "${duplicates[@]}"; do
478    rm -v "${base_dir}/${entry}"
479    typeset -i i="0"
480    for i in "${!all_files[@]}"; do
481      typeset all_entry="${all_files[${i}]}"
482      typeset relative_path="${all_entry##"$(lazy_canonical_path "${base_dir}/")"}"
483      if [ "${relative_path}" = "${entry}" ]; then
484        unset all_files[${i}]
485      fi
486    done
487  done
488
489  echo "New value for all_files:"
490  for entry in "${all_files[@]}"; do
491    echo "${entry}"
492  done
493
494  echo "Duplicates-to-real map:"
495  # Build complementary array to duplicates.
496  typeset -a to_files
497  for entry in "${duplicates[@]}"; do
498    typeset filename="$(basename "${entry}")"
499
500    typeset all_entry=""
501    for all_entry in "${all_files[@]}"; do
502      typeset all_entry_filename="$(basename "${all_entry}")"
503
504      if [ -n "${filename}" ] && [ -n "${all_entry_filename}" ]; then
505        if [ "${filename}" = "${all_entry_filename}" ]; then
506          typeset dependency_format="$(lazy_canonical_path "${dependency_base_format}/${all_entry##${base_dir}}")"
507          to_files+=( "${dependency_format}" )
508
509          echo "${entry} => ${dependency_format}"
510
511          # There should be only one entry matching, so we can save a bit of time and break out of the loop.
512          # Even more importantly, we only want one entry for each duplicates entry anyway...
513          break
514        fi
515      else
516        echo "ERROR: empty file name while matching duplicates with non-duplicates." >&2
517        echo "ERROR: duplicate entry: \"${entry}\"" >&2
518        echo "ERROR: real entry: \"${all_entry}\"" >&2
519        exit 1
520      fi
521    done
522  done
523
524  # Add binaries to all_files as well.
525  typeset entry=""
526  while read -r -d '' entry; do
527    echo "Adding ${entry} to all files"
528    all_files+=( "${entry}" )
529  done < <(gfind "${EXE_DIR}" -type 'f' -executable -print0)
530
531  # Try to fixup files broken by duplicates removal.
532  for all_entry in "${all_files[@]}"; do
533    typeset otool_out=""
534    typeset -i tmp_ret="0"
535
536    # Newer otool versions terminate with a non-zero return code on errors,
537    # while the classic/legacy versions do not. We need to make sure our
538    # script doesn't terminate just because otool returns a non-zero exit
539    # status.
540    set +e
541    otool_out="$(otool -L "${all_entry}")"
542    tmp_ret="${?}"
543    set -e
544
545    # If the return code was non-zero, skip this file.
546    # A return code of zero does not automatically mean that otool finished
547    # successfully, so in that case throw otool's stdout into parse_otool_output().
548    if [ "${tmp_ret}" -eq "0" ]; then
549      # Don't merge the declaration and initialization with the real value assignment.
550      # We need the return value of parse_otool_output(), but running
551      # typeset foo="$(bar)" will give us the return value of typeset, not bar().
552      typeset dependencies=""
553      set +e
554      dependencies="$(parse_otool_output "${otool_out}")"
555      tmp_ret="${?}"
556      set -e
557    fi
558
559    if [ "${tmp_ret}" -ne "0" ]; then
560      echo "WARNING: otool returned error for file: ${all_entry}" >&2
561      echo "WARNING: skipping." >&2
562      continue
563    fi
564
565    typeset line=""
566    while read -r line; do
567      #echo "dependency of ${all_entry}: ${line}"
568
569      typeset duplicate_entry=""
570      typeset -i i="0"
571      for i in "${!duplicates[@]}"; do
572        typeset duplicate_entry="${duplicates[${i}]}"
573        #echo "checking for duplicate ${duplicate_entry}"
574        typeset duplicate_format="$(lazy_canonical_path "${dependency_base_format}/${duplicate_entry}")"
575
576        if [ -n "${line}" ] && [ -n "${duplicate_format}" ]; then
577          if [ "${line}" = "${duplicate_format}" ]; then
578            install_name_tool -change "${line}" "${to_files[${i}]}" "${all_entry}"
579          fi
580        else
581          echo "ERROR: empty file name while replacing duplicate dependencies." >&2
582          echo "ERROR: for file ${all_entry}" >&2
583          echo "ERROR: at dependency ${line}" >&2
584          echo "ERROR: duplicate entry: \"${duplicate_entry}\"" >&2
585          echo "ERROR: dependency: \"${line}\"" >&2
586          exit 1
587        fi
588      done
589    done <<< "${dependencies}"
590  done
591
592  phase "Bundling up using macdeployqt"
593  macdeployqt "${APPBUNDLE}" -verbose=2
594
595  phase "Creating DMG"
596  ${PKG_DMG} \
597    --source "${APPBUNDLE}" \
598    --sourcefile \
599    --target "${DMGFILE}" \
600    --volname "x2goclient" \
601    --verbosity 2 \
602    --mkdir "/.background" \
603    --copy "${TOP_DIR}/res/img/png/macinstaller_background.png:/.background" \
604    --copy "${TOP_DIR}/res/osxbundle/macdmg.DS_Store:/.DS_Store" \
605    --copy "${TOP_DIR}/LICENSE" \
606    --copy "${TOP_DIR}/COPYING" \
607    --symlink "/Applications" \
608    --icon "${TOP_DIR}/res/img/icons/dmg/x2go-mac-dmg.icns" \
609    --format "UDBZ"
610fi
611
612popd
613