File: xcode-utils.sh

package info (click to toggle)
keyman 18.0.246-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 21,316 kB
  • sloc: python: 52,784; cpp: 21,289; sh: 7,633; ansic: 4,823; xml: 3,617; perl: 959; makefile: 139; javascript: 138
file content (178 lines) | stat: -rwxr-xr-x 8,091 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env bash

# This script will automatically have Xcode's build environment (and variables),
# so there's no need to do anything extra to fetch them.

# Some fun details for interacting with Xcode:
#  - echo "warning: foo" will generate an actual compile warning within Xcode:  "foo"
#  - echo "error: bar" will likewise generate a compile error within Xcode: "bar"

set -e

function buildWarning() {
  echo "warning: $1"
}

function buildError() {
  echo "error: $1"
}

# Imports the autogenerated environment.sh from command-line builds.
function importEnvironment() {
  # Requires that KEYMAN_ROOT is set... which it should be, if a build is successfully
  if [ -z "${KEYMAN_ROOT:-}" ]; then
    buildError "KEYMAN_ROOT is not defined.  Recommendation:  define it as a project-wide \"user-defined\" Build Setting for the \"$PROJECT_NAME\" project."
    exit 1
  else
    # As defined within build-utils.sh, which this script does NOT call.  (It was already called externally.)
    ENVIRONMENT_SH="$KEYMAN_ROOT/resources/environment.sh"
    . "$ENVIRONMENT_SH"
  fi
}

# Build Phase:  "Set Bundle Versions"
# Takes one parameter:  "TAGGED".  Defaults to false (iOS: KMEI, SWKeyboard), sets tagged info if true (iOS: Keyman)
#
# Thanks to https://medium.com/@bestiosdevelope/automatic-build-incrementation-technique-for-ios-release-94eb0d08785b
# for its documentation in this matter.
function phaseSetBundleVersions() {
  if [[ $# -gt 0 ]]; then
    TAGGED="$1" # Should be just `true`, unless someone gets 'creative' later.  But we compare against `true`.
  else
    TAGGED=false
  fi

  # For command-line builds, VERSION and VERSION_WITH_TAG) are forwarded through xcodebuild.
  if [ -z "${VERSION:-}" ]; then
    # This is likely not a command-line build at the top level; it's been triggered through Xcode.
    # The $VERSION-loading code is in build-utils.sh, but we need $THIS_SCRIPT to be set
    # (in similar manner to the "standard build header") in order to avoid script errors therein.
    THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"

    # Note that this script's process will not have access to TC environment variables, but that's fine for
    # local builds triggered through Xcode's UI, which aren't part of our CI processes.
    . "$KEYMAN_ROOT/resources/build/build-utils.sh"
    echo "phaseSetBundleVersions: UI build - fetching version from repository:"
    echo "  Plain:  $VERSION"
    echo "  Tagged: $VERSION_WITH_TAG"
    echo "  Environment: $VERSION_ENVIRONMENT"
  else
    echo "phaseSetBundleVersions: Command-line build - using provided version parameters"
  fi

  # Now, to set the build number (CFBundleVersion)
  # 1.0 is the default for all released builds. For PRs, we use 0.PR#.n, and
  # for n, use the TeamCity build.counter variable surfaced in the env var
  # TEAMCITY_BUILD_COUNTER to give us a unique build id. Note that
  # CFBundleVersion cannot be longer than 18 characters.

  BUILD_NUMBER=1.0
  if [ ! -z "${TEAMCITY_PR_NUMBER-}" ]; then
    if [[ $TEAMCITY_PR_NUMBER =~ ^[0-9]+$ ]]; then
      BUILD_NUMBER=0.$TEAMCITY_PR_NUMBER.$TEAMCITY_BUILD_COUNTER
    fi
  fi

  APP_PLIST="$TARGET_BUILD_DIR/$INFOPLIST_PATH"
  echo "phaseSetBundleVersions: Setting CFBundleVersion to $BUILD_NUMBER for $APP_PLIST"
  /usr/libexec/Plistbuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$APP_PLIST"
  echo "phaseSetBundleVersions: Setting CFBundleShortVersionString to $VERSION for $APP_PLIST"
  /usr/libexec/Plistbuddy -c "Set :CFBundleShortVersionString $VERSION" "$APP_PLIST"

  /usr/libexec/Plistbuddy -c "Print" "$APP_PLIST"

  # Only attempt to write this when directly specified (otherwise, generates minor warning)
  if [ $TAGGED == true ]; then
    echo "phaseSetBundleVersions: Setting KeymanVersionWithTag to $VERSION_WITH_TAG for tagged version for $APP_PLIST"
    /usr/libexec/Plistbuddy -c "Set :KeymanVersionWithTag $VERSION_WITH_TAG" "$APP_PLIST"
    echo "phaseSetBundleVersions: Setting KeymanVersionEnvironment to $VERSION_ENVIRONMENT for $APP_PLIST"
    /usr/libexec/Plistbuddy -c "Set :KeymanVersionEnvironment $VERSION_ENVIRONMENT" "$APP_PLIST"
  fi

  if [ -f "${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}.dSYM/Contents/Info.plist" ]; then
    DSYM_PLIST="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}.dSYM/Contents/Info.plist"
    echo "phaseSetBundleVersions: Setting CFBundleVersion to $BUILD_NUMBER for $DSYM_PLIST"
    /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$DSYM_PLIST"
    echo "phaseSetBundleVersions: Setting CFBundleShortVersionString to $VERSION for $DSYM_PLIST"
    /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$DSYM_PLIST"
  fi
}

# Used by Keyman for iOS to update the human-readable string for its Settings screen.
function setSettingsBundleVersion() {
  # For command-line builds, VERSION and VERSION_WITH_TAG) are forwarded through xcodebuild.
  if [ -z "${VERSION:-}" ]; then
    # We're not a command-line build... so we'll need to retrieve these values ourselves with ./build-utils.sh.
    # Note that this script's process will not have access to TC environment variables, but that's fine for
    # local builds triggered through Xcode's UI, which aren't part of our CI processes.
    . "$KEYMAN_ROOT/resources/build/build-utils.sh"
    echo "setSettingsBundleVersion: UI build - fetching version from repository:"
    echo "  Plain:  $VERSION"
    echo "  Tagged: $VERSION_WITH_TAG"
    echo "  Environment: (not setting, assume 'local')"
  else
    echo "setSettingsBundleVersion: Command-line build - using provided version parameters"
  fi

  SETTINGS_PLIST="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle/Root.plist"
  echo "setSettingsBundleVersion: Setting $VERSION_WITH_TAG for $SETTINGS_PLIST"
  # We assume that entry 0 = the version "preference" entry.
  /usr/libexec/PlistBuddy -c "Set :PreferenceSpecifiers:0:DefaultValue $VERSION_WITH_TAG" "$SETTINGS_PLIST"
}

# Build Phase:  Upload dSYM (debug) files to Sentry
# Takes one parameter - the SENTRY_PROJECT target.  The other parameter needs to be set as an "input file" within Xcode.
function phaseSentryDsymUpload() {

  if [[ $# -gt 0 ]]; then
    SENTRY_PROJECT_TARGET="$1" # should only be `keyman-ios`, `keyman-mac`, or the like, but just in case.
  else
    buildError "SENTRY_PROJECT parameter was not provided to Sentry upload utility function."
    exit 1
  fi

  # Import environment information!
  importEnvironment
  if [ $? -ne 0 ]; then
    exit 1
  fi

  # Reference for this detection check for the input file: https://www.iosdev.recipes/xcode/input-output-files/
  if [ -z "${SCRIPT_INPUT_FILE_0:-}" ]; then
    buildError "Run Script must have an input file set: \${DWARF_DSYM_FOLDER_PATH}/\${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/\${TARGET_NAME}"
    exit 1
  fi

  if [ -z "${SENTRY_AUTH_TOKEN:-}" ]; then
    # Left as a warning so that builds aren't blocked for random contributors.
    # Likewise for the Sentry errors later.
    buildWarning "Cannot successfully upload the dSYM to sentry if a Sentry auth token is not provided."
  fi

  # The remaining update logic seen here was auto-generated at https://sentry.keyman.com/keyman/keyman-ios/getting-started/cocoa-swift/
  if which sentry-cli >/dev/null; then
    export SENTRY_PROJECT="$SENTRY_PROJECT_TARGET"
    export SENTRY_LOG_LEVEL=info
    ERROR=$(sentry-cli upload-dif "$DWARF_DSYM_FOLDER_PATH" 2>&1 >/dev/null)
    if [ ! $? -eq 0 ]; then
      buildWarning "sentry-cli - $ERROR"
    fi
  else
    buildWarning "sentry-cli not installed; please run \"brew install getsentry/tools/sentry-cli\" to remedy"
  fi
}

#
# All calls to xcode-utils.sh scripts will have their output redirected to
# $KEYMAN_ROOT/xcodebuild-scripts.log. This will redirect both stdout and stderr
# to this log file. See the corresponding printXCodeBuildScriptLogs function in
# build-utils.sh to print the log after xcodebuild returns.
#
# More information in the build-utils.sh.
#
function logScriptsToFile() {
  local SCRIPT_LOG="$KEYMAN_ROOT/xcodebuild-scripts.log"
  exec >> "$SCRIPT_LOG" 2>&1
}

logScriptsToFile