1#!/usr/local/bin/bash
2
3if [ "$1" ==  "-fromScript" ]; then
4  otherScript=$2
5  shift 2
6  echo Executed from $otherScript with arguments: $*
7fi
8
9build=false
10ci=false
11configuration="Debug"
12help=false
13nolog=false
14pack=false
15prepareMachine=false
16rebuild=false
17norestore=false
18sign=false
19skipTests=false
20bootstrapOnly=false
21verbosity="minimal"
22hostType="core"
23properties=""
24dotnetBuildFromSource=false
25dotnetCoreSdkDir=""
26
27function Help() {
28  echo "Common settings:"
29  echo "  -configuration <value>  Build configuration Debug, Release"
30  echo "  -host <value>           core (default), mono"
31  echo "  -verbosity <value>      Msbuild verbosity (q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic])"
32  echo "  -help                   Print help and exit"
33  echo ""
34  echo "Actions:"
35  echo "  -norestore              Don't automatically run restore"
36  echo "  -build                  Build solution"
37  echo "  -rebuild                Rebuild solution"
38  echo "  -skipTests              Don't run tests"
39  echo "  -bootstrapOnly          Don't run build again with bootstrapped MSBuild"
40  echo "  -sign                   Sign build outputs"
41  echo "  -pack                   Package build outputs into NuGet packages and Willow components"
42  echo ""
43  echo "Advanced settings:"
44  echo "  -ci                     Set when running on CI server"
45  echo "  -nolog                  Disable logging"
46  echo "  -prepareMachine         Prepare machine for CI run"
47  echo "  -useSystemMSBuild       [mono] Use system msbuild instead of downloading a copy for use with mono"
48  echo ""
49  echo "Command line arguments not listed above are passed through to MSBuild."
50}
51
52while [[ $# -gt 0 ]]; do
53  lowerI="$(echo $1 | awk '{print tolower($0)}')"
54  case $lowerI in
55    build)
56      build=true
57      shift 1
58      ;;
59    -build)
60      build=true
61      shift 1
62      ;;
63    -ci)
64      ci=true
65      shift 1
66      ;;
67    -configuration)
68      configuration=$2
69      shift 2
70      ;;
71    -h | -help)
72      Help
73      exit 0
74      ;;
75    -host)
76      hostType=$2
77      shift 2
78      ;;
79    -nolog)
80      nolog=true
81      shift 1
82      ;;
83    -pack)
84      pack=true
85      shift 1
86      ;;
87    -preparemachine)
88      prepareMachine=true
89      shift 1
90      ;;
91    -rebuild)
92      rebuild=true
93      shift 1
94      ;;
95    -norestore)
96      norestore=true
97      shift 1
98      ;;
99    -sign)
100      sign=true
101      shift 1
102      ;;
103    -skiptests)
104      skipTests=true
105      shift 1
106      ;;
107    -bootstraponly)
108      bootstrapOnly=true
109      shift 1
110      ;;
111    -usesystemmsbuild)
112      useSystemMSBuild=true
113      shift 1
114      ;;
115    -verbosity)
116      verbosity=$2
117      shift 2
118      ;;
119    -dotnetbuildfromsource)
120      dotnetBuildFromSource=true
121      shift 1
122      ;;
123    -dotnetcoresdkdir)
124      dotnetCoreSdkDir=$2
125      shift 2
126      ;;
127    *)
128      properties="$properties $1"
129      shift 1
130      ;;
131  esac
132done
133
134if [ "$hostType" = "mono" ]; then
135  configuration="$configuration-MONO"
136fi
137
138function KillProcessWithName {
139  echo "Killing processes containing \"$1\""
140  kill $(ps ax | grep -i "$1" | awk '{ print $1 }')
141}
142
143function StopProcesses {
144  echo "Killing running build processes..."
145  KillProcessWithName "dotnet"
146  KillProcessWithName "vbcscompiler"
147}
148
149function ExitIfError {
150  if [ $1 != 0 ]
151  then
152    echo "$2"
153
154    if [[ "$ci" != "true" && "$dotnetBuildFromSource" != "true" && "$hostType" != "mono" ]]; # kill command not permitted on CI machines or in source-build
155    then
156      StopProcesses
157    fi
158
159    exit $1
160  fi
161}
162
163function CreateDirectory {
164  if [ ! -d "$1" ]
165  then
166    mkdir -p "$1"
167  fi
168}
169
170function QQ {
171  echo '"'$*'"'
172}
173
174function GetLogCmd {
175  if $ci || $log
176  then
177    CreateDirectory $LogDir
178
179    local logCmd="/bl:$(QQ $LogDir/$1.binlog)"
180
181    # When running under CI, also create a text log, so it can be viewed in the Jenkins UI
182    if $ci
183    then
184      logCmd="$logCmd /fl /flp:Verbosity=diag\;LogFile=$(QQ $LogDir/$1.log)"
185    fi
186  else
187    logCmd=""
188  fi
189
190  echo $logCmd
191}
192
193function downloadMSBuildForMono {
194  if [[ -z $useSystemMSBuild && ! -e "$MONO_MSBUILD_DIR/MSBuild.dll" ]]
195  then
196    echo "** Downloading MSBUILD from $MSBUILD_DOWNLOAD_URL"
197    curl -sL -o "$MSBUILD_ZIP" "$MSBUILD_DOWNLOAD_URL"
198
199    unzip -q "$MSBUILD_ZIP" -d "$ArtifactsDir"
200    # rename just to make it obvious when reading logs!
201    mv $ArtifactsDir/msbuild $ArtifactsDir/mono-msbuild
202    chmod +x $ArtifactsDir/mono-msbuild/MSBuild.dll
203    rm "$MSBUILD_ZIP"
204  fi
205}
206
207function CallMSBuild {
208  local commandLine=""
209
210  if [ -z "$msbuildHost" ]
211  then
212    commandLine="$msbuildToUse $*"
213  else
214    commandLine="$msbuildHost $msbuildToUse $*"
215  fi
216
217  echo ============= MSBuild command =============
218  echo "$commandLine"
219  echo ===========================================
220
221  eval $commandLine
222
223  ExitIfError $? "Failed to run MSBuild: $commandLine"
224}
225
226function GetVersionsPropsVersion {
227  echo "$( awk -F'[<>]' "/<$1>/{print \$3}" "$VersionsProps" )"
228}
229
230function DownloadDotnetCli {
231  DotNetCliVersion="$( GetVersionsPropsVersion DotNetCliVersion )"
232  DotNetInstallVerbosity=""
233
234  if [ -z "$DOTNET_INSTALL_DIR" ]
235  then
236    export DOTNET_INSTALL_DIR="$RepoRoot/artifacts/.dotnet/$DotNetCliVersion"
237  fi
238
239  DotNetRoot=$DOTNET_INSTALL_DIR
240  DotNetInstallScript="$DotNetRoot/dotnet-install.sh"
241
242  if [ ! -a "$DotNetInstallScript" ]
243  then
244    CreateDirectory "$DotNetRoot"
245    curl "https://dot.net/v1/dotnet-install.sh" -sSL -o "$DotNetInstallScript"
246  fi
247
248  if [[ "$(echo $verbosity | awk '{print tolower($0)}')" == "diagnostic" ]]
249  then
250    DotNetInstallVerbosity="--verbose"
251  fi
252
253  # Install a stage 0
254  SdkInstallDir="$DotNetRoot/sdk/$DotNetCliVersion"
255
256  if [ ! -d "$SdkInstallDir" ]
257  then
258    bash "$DotNetInstallScript" --version $DotNetCliVersion $DotNetInstallVerbosity
259    LASTEXITCODE=$?
260
261    if [ $LASTEXITCODE != 0 ]
262    then
263      echo "Failed to install stage0"
264      return $LASTEXITCODE
265    fi
266  fi
267}
268
269function InstallDotNetCli {
270  if [ "$dotnetCoreSdkDir" = "" ]
271  then
272    DownloadDotnetCli
273  else
274    export DOTNET_INSTALL_DIR=$dotnetCoreSdkDir
275  fi
276
277  # don't double quote this otherwise the csc tooltask will fail with double double-quotting
278  export DOTNET_HOST_PATH="$DOTNET_INSTALL_DIR/dotnet"
279
280  # Put the stage 0 on the path
281  export PATH="$DOTNET_INSTALL_DIR:$PATH"
282
283  # Disable first run since we want to control all package sources
284  export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
285
286  # Don't resolve runtime, shared framework, or SDK from other locations
287  export DOTNET_MULTILEVEL_LOOKUP=0
288}
289
290function InstallRepoToolset {
291  RepoToolsetVersion="$( GetVersionsPropsVersion RoslynToolsRepoToolsetVersion )"
292  RepoToolsetDir="$NuGetPackageRoot/roslyntools.repotoolset/$RepoToolsetVersion/tools"
293  RepoToolsetBuildProj="$RepoToolsetDir/Build.proj"
294
295  local logCmd=$(GetLogCmd Toolset)
296
297  if [ ! -d "$RepoToolsetBuildProj" ]
298  then
299    ToolsetProj="$ScriptRoot/Toolset.proj"
300    CallMSBuild $(QQ $ToolsetProj) /t:restore /m /clp:Summary /warnaserror /v:$verbosity $logCmd $properties
301  fi
302}
303
304function ErrorHostType {
305  echo "Unsupported hostType ($hostType)"
306  exit 1
307}
308
309function Build {
310  if [ "$host" = "core" ]; then
311    InstallDotNetCli
312    echo "Using dotnet from: $DOTNET_INSTALL_DIR"
313  else
314    CreateDirectory $ArtifactsDir
315  fi
316
317  if $prepareMachine
318  then
319    CreateDirectory "$NuGetPackageRoot"
320    eval "$(QQ $DOTNET_HOST_PATH) nuget locals all --clear"
321
322    ExitIfError $? "Failed to clear NuGet cache"
323  fi
324
325  if [ "$hostType" = "core" ]
326  then
327    msbuildHost=$(QQ $DOTNET_HOST_PATH)
328  elif [ "$hostType" = "mono" ]
329  then
330    if [ -z $useSystemMSBuild ]; then
331      downloadMSBuildForMono
332      msbuildHost=""
333      msbuildToUse="$MONO_MSBUILD_DIR/msbuild"
334    else
335      msbuildHost=""
336      msbuildToUse="msbuild"
337    fi
338  else
339    ErrorHostType
340  fi
341
342  InstallRepoToolset
343
344  local logCmd=$(GetLogCmd Build)
345
346  commonMSBuildArgs="/m /clp:Summary /v:$verbosity /p:Configuration=$configuration /p:SolutionPath=$(QQ $MSBuildSolution) /p:CIBuild=$ci /p:DisableNerdbankVersioning=true"
347
348  # Only enable warnaserror on CI runs.
349  if $ci
350  then
351    commonMSBuildArgs="$commonMSBuildArgs /warnaserror"
352  fi
353
354  # Only test using stage 0 MSBuild if -bootstrapOnly is specified
355  local testStage0=false
356  if $bootstrapOnly
357  then
358    testStage0=$test
359  fi
360
361  CallMSBuild $(QQ $RepoToolsetBuildProj) $commonMSBuildArgs $logCmd /p:Restore=$restore /p:Build=$build /p:Rebuild=$rebuild /p:Test=$testStage0 /p:Sign=$sign /p:Pack=$pack /p:CreateBootstrap=true $properties
362
363  if ! $bootstrapOnly
364  then
365    bootstrapRoot="$ArtifactsConfigurationDir/bootstrap"
366
367    if [ $hostType = "core" ]
368    then
369      msbuildToUse=$(QQ "$bootstrapRoot/netcoreapp2.1/MSBuild/MSBuild.dll")
370    elif [ "$hostType" = "mono" ]
371    then
372      msbuildToUse="$bootstrapRoot/net461/MSBuild/15.0/Bin/MSBuild.dll"
373      msbuildHost="mono"
374
375      properties="$properties /p:MSBuildExtensionsPath=$bootstrapRoot/net461/MSBuild/"
376    else
377      ErrorHostType
378    fi
379
380    # forcing the slashes here or they get normalized and lost later
381    export ArtifactsDir="$ArtifactsDir\\2\\"
382
383    local logCmd=$(GetLogCmd BuildWithBootstrap)
384
385      # When using bootstrapped MSBuild:
386      # - Turn off node reuse (so that bootstrapped MSBuild processes don't stay running and lock files)
387      # - Don't sign
388      # - Don't pack
389      # - Do run tests (if not skipped)
390      # - Don't try to create a bootstrap deployment
391    CallMSBuild $(QQ $RepoToolsetBuildProj) $commonMSBuildArgs /nr:false $logCmd /p:Restore=$restore /p:Build=$build /p:Rebuild=$rebuild /p:Test=$test /p:Sign=false /p:Pack=false /p:CreateBootstrap=false $properties
392  fi
393}
394
395function AssertNugetPackages {
396  packageCount=$(find $PackagesDir -type f | wc -l)
397  if $pack || $dotnetBuildFromSource
398  then
399
400    if [ $packageCount -ne 5 ]
401    then
402      ExitIfError 1 "Did not find 5 packages in $PackagesDir"
403    fi
404
405    echo "Ensured that 5 nuget packages were created"
406  fi
407}
408
409SOURCE="${BASH_SOURCE[0]}"
410while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
411  ScriptRoot="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
412  SOURCE="$(readlink "$SOURCE")"
413  [[ $SOURCE != /* ]] && SOURCE="$ScriptRoot/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
414done
415ScriptRoot="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
416
417RepoRoot="$ScriptRoot/.."
418ArtifactsDir="$RepoRoot/artifacts"
419ArtifactsConfigurationDir="$ArtifactsDir/$configuration"
420PackagesDir="$ArtifactsConfigurationDir/packages"
421LogDir="$ArtifactsConfigurationDir/log"
422VersionsProps="$ScriptRoot/Versions.props"
423
424MONO_MSBUILD_DIR="$ArtifactsDir/mono-msbuild"
425MSBUILD_DOWNLOAD_URL="https://github.com/mono/msbuild/releases/download/v0.05/mono_msbuild_port2-394a6b5e.zip"
426MSBUILD_ZIP="$ArtifactsDir/msbuild.zip"
427
428#https://github.com/dotnet/source-build
429if $dotnetBuildFromSource
430then
431  MSBuildSolution="$RepoRoot/MSBuild.SourceBuild.sln"
432else
433  MSBuildSolution="$RepoRoot/MSBuild.sln"
434fi
435
436msbuildToUse="msbuild"
437
438log=false
439if ! $nolog
440then
441  log=true
442fi
443
444restore=false
445if ! $norestore
446then
447  restore=true
448fi
449
450test=false
451if ! $skipTests
452then
453  test=true
454fi
455
456if [ "$hostType" != "core" -a "$hostType" != "mono" ]; then
457  ErrorHostType
458fi
459
460# HOME may not be defined in some scenarios, but it is required by NuGet
461if [ -z $HOME ]
462then
463  export HOME="$RepoRoot/artifacts/.home/"
464  CreateDirectory "$HOME"
465fi
466
467if $ci
468then
469  TempDir="$ArtifactsConfigurationDir/tmp"
470  CreateDirectory "$TempDir"
471
472  export TEMP="$TempDir"
473  export TMP="$TempDir"
474  export MSBUILDDEBUGPATH="$TempDir"
475fi
476
477if [ -z $NUGET_PACKAGES ]
478then
479  export NUGET_PACKAGES="$HOME/.nuget/packages"
480fi
481
482NuGetPackageRoot=$NUGET_PACKAGES
483
484Build
485
486ExitIfError $? "Build failed"
487
488AssertNugetPackages
489
490ExitIfError $? "AssertNugetPackages failed"
491