aboutsummaryrefslogtreecommitdiff
path: root/msvc-common
blob: 096b17f0febf810e0a106a6aade32d6160324120 (plain)
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
#! /usr/bin/env bash

# Note: shouldn't be executed directly.

# Translate absolute POSIX path to a Windows path with winepath.
#
function translate () # <path>
{
  if [[ "$1" == /* ]]; then
    winepath -w "$1"
  else
    echo "$1"
  fi
}

# Split the combined option and path value, translate the path component
# to a Windows path if absolute, then recombine the option and path.
#
function split_translate () # <length> <option-path>
{
  local o="${2:0:$1}" # First <length> characters from $1.
  local v="${2:$1}"   # The rest.

  # If the path is absolute, map it with winepath.
  #
  if [[ "$v" == /* ]]; then
    v="$(winepath -w "$v")"
  fi

  echo "$o$v"
}

# The <diag> argument should be 1 or 2. It indicates whether the diagnostics
# is sent to stdout (1) or stderr (2).
#
# Note that if <exe> returns non-zero exit status, then this function calls
# exit, not return. It also clears the ERR trap and overrides the EXIT trap.
# All this pretty much means it should be the last statement in a call.
#
function msvc_exec () # <diag> <exe> <arg>...
{
  local diag=$1
  shift

  local exe="$1"
  shift

  # Assemble the arguments in an array to store in case they contain spaces.
  #
  local args=()

  while [ $# -gt 0 ]; do
    args=("${args[@]}" "$1")
    shift
  done

  # Translate absolute Windows paths back to POSIX. The hard part here is to
  # determing the end of the path. For example, the error location has the
  # 'X:\...\foo(10):' form. However, we cannot assume that '(' ends the path;
  # remember 'Program Files (x86)'.
  #
  # To sidestep this whole mess we are going to use this trick: instead of
  # translating the whole path we will only translate its directory part, that
  # is the longest part that still ends with the directory separator. We will
  # also still recognize ':' and ''' as path terminators as well as space if
  # it is the first character in the component.
  #
  # We also pass the path through realpath in order to get the actual path
  # rather than Wine's dosdevices links.
  #

  # First delimit paths that we need to translate with NUL characters.
  #
  local s1="s#[A-Za-z]:[\\/]([^ ':][^':]*[\\/])*#\x00&\x00#g"

  # Next translate the paths (note the -z sed option). The last xargs call
  # does two things: it removes the newline added by realpath and adds the
  # trailing slash removed by realpath.
  #
  # Substitution useful for debugging: #/bin/echo -n '&'#
  #
  local s2="s#^[A-Za-z]:[\\/]([^ ':][^':]*[\\/])*#winepath -u0 '&' | \
xargs -0 realpath -z | xargs -0 -I{} /bin/echo -n {}/#e"

  # Finally, get rid of the NUL characters. While at it, also kill Windows
  # CR (0x0d).
  #
  local s3="s#\x00##g;s#\x0d##g"

  # For testing/debugging:
  #
  #cat input | sed -re "$s1" | sed -z -re "$s2" | sed -re "$s3"

  # Suppress Wine noise.
  #
  export WINEDEBUG=fixme-all

  # Create a temporary named pipe.
  #
  local pipe
  pipe="$(mktemp -u)"
  mkfifo $pipe
  trap "{ rm $pipe; }" EXIT

  if [ $diag -eq 1 ]; then
    wine "$exe" "${args[@]}" 2>&1 1>$pipe &
    sed -re "$s1" $pipe | sed -z -re "$s2" | sed -re "$s3"
  else
    # For some reason Wine is really slow when we redirect stdout to
    # /dev/null. A lot slower than redirecting it to a file. This is observed
    # with Wine 1.7, 1.8, and 1.9. As an admittedly bizarre workaround we are
    # going to channel the output via a fifo. Yes, it does help, a lot.
    #
    local opipe
    opipe="$(mktemp -u)"
    mkfifo $opipe
    trap "{ rm $pipe $opipe; }" EXIT

    cat $opipe &
    local pid=$!

    wine "$exe" "${args[@]}" 2>$pipe >$opipe &
    sed -re "$s1" $pipe | sed -z -re "$s2" | sed -re "$s3" 1>&2

    # Wait for cat. If it fails then the ERR trap will terminate us.
    #
    wait $pid
  fi

  # Wait for the wine process and exit with its exit status if it's not
  # zero. Don't you just hate bash sometimes? I sure do.
  #
  trap - ERR
  wait $!
  local r=$?
  if [ $r -ne 0 ]; then
    exit $r
  fi
}