1#!/usr/bin/env bash
2#===-- test-release.sh - Test the LLVM release candidates ------------------===#
3#
4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5# See https://llvm.org/LICENSE.txt for license information.
6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7#
8#===------------------------------------------------------------------------===#
9#
10# Download, build, and test the release candidate for an LLVM release.
11#
12#===------------------------------------------------------------------------===#
13
14System=`uname -s`
15if [ "$System" = "FreeBSD" ]; then
16    MAKE=gmake
17else
18    MAKE=make
19fi
20generator="Unix Makefiles"
21
22Release=""
23Release_no_dot=""
24RC=""
25Triple=""
26use_gzip="no"
27do_checkout="yes"
28do_debug="no"
29do_asserts="no"
30do_compare="yes"
31do_rt="yes"
32do_clang_tools="yes"
33do_libs="yes"
34do_libcxxabi="yes"
35do_libunwind="yes"
36do_test_suite="yes"
37do_openmp="yes"
38do_lld="yes"
39do_lldb="yes"
40do_polly="yes"
41do_mlir="yes"
42do_flang="yes"
43do_silent_log="no"
44BuildDir="`pwd`"
45ExtraConfigureFlags=""
46ExportBranch=""
47git_ref=""
48
49function usage() {
50    echo "usage: `basename $0` -release X.Y.Z -rc NUM [OPTIONS]"
51    echo ""
52    echo " -release X.Y.Z       The release version to test."
53    echo " -rc NUM              The pre-release candidate number."
54    echo " -final               The final release candidate."
55    echo " -triple TRIPLE       The target triple for this machine."
56    echo " -j NUM               Number of compile jobs to run. [default: 3]"
57    echo " -build-dir DIR       Directory to perform testing in. [default: pwd]"
58    echo " -no-checkout         Don't checkout the sources from SVN."
59    echo " -test-debug          Test the debug build. [default: no]"
60    echo " -test-asserts        Test with asserts on. [default: no]"
61    echo " -no-compare-files    Don't test that phase 2 and 3 files are identical."
62    echo " -use-gzip            Use gzip instead of xz."
63    echo " -use-ninja           Use ninja instead of make/gmake."
64    echo " -configure-flags FLAGS  Extra flags to pass to the configure step."
65    echo " -git-ref sha         Use the specified git ref for testing instead of a release."
66    echo " -no-rt               Disable check-out & build Compiler-RT"
67    echo " -no-clang-tools      Disable check-out & build clang-tools-extra"
68    echo " -no-libs             Disable check-out & build libcxx/libcxxabi/libunwind"
69    echo " -no-libcxxabi        Disable check-out & build libcxxabi"
70    echo " -no-libunwind        Disable check-out & build libunwind"
71    echo " -no-test-suite       Disable check-out & build test-suite"
72    echo " -no-openmp           Disable check-out & build libomp"
73    echo " -no-lld              Disable check-out & build lld"
74    echo " -lldb                Enable check-out & build lldb"
75    echo " -no-lldb             Disable check-out & build lldb (default)"
76    echo " -no-polly            Disable check-out & build Polly"
77    echo " -no-mlir             Disable check-out & build MLIR"
78    echo " -no-flang            Disable check-out & build Flang"
79    echo " -silent-log          Don't output build logs to stdout"
80}
81
82while [ $# -gt 0 ]; do
83    case $1 in
84        -release | --release )
85            shift
86            Release="$1"
87            Release_no_dot="`echo $1 | sed -e 's,\.,,g'`"
88            ;;
89        -rc | --rc | -RC | --RC )
90            shift
91            RC="rc$1"
92            ;;
93        -final | --final )
94            RC=final
95            ;;
96        -git-ref | --git-ref )
97            shift
98            Release="test"
99            Release_no_dot="test"
100            ExportBranch="$1"
101            RC="`echo $ExportBranch | sed -e 's,/,_,g'`"
102            git_ref="$1"
103            echo "WARNING: Using the ref $git_ref instead of a release tag"
104            echo "         This is intended to aid new packagers in trialing "
105            echo "         builds without requiring a tag to be created first"
106            ;;
107        -triple | --triple )
108            shift
109            Triple="$1"
110            ;;
111        -configure-flags | --configure-flags )
112            shift
113            ExtraConfigureFlags="$1"
114            ;;
115        -j* )
116            NumJobs="`echo $1 | sed -e 's,-j\([0-9]*\),\1,g'`"
117            if [ -z "$NumJobs" ]; then
118                shift
119                NumJobs="$1"
120            fi
121            ;;
122        -use-ninja )
123            MAKE=ninja
124            generator=Ninja
125            ;;
126        -build-dir | --build-dir | -builddir | --builddir )
127            shift
128            BuildDir="$1"
129            ;;
130        -no-checkout | --no-checkout )
131            do_checkout="no"
132            ;;
133        -test-debug | --test-debug )
134            do_debug="yes"
135            ;;
136        -test-asserts | --test-asserts )
137            do_asserts="yes"
138            ;;
139        -no-compare-files | --no-compare-files )
140            do_compare="no"
141            ;;
142        -use-gzip | --use-gzip )
143            use_gzip="yes"
144            ;;
145        -no-rt )
146            do_rt="no"
147            ;;
148        -no-libs )
149            do_libs="no"
150            ;;
151        -no-clang-tools )
152            do_clang_tools="no"
153            ;;
154        -no-libcxxabi )
155            do_libcxxabi="no"
156            ;;
157        -no-libunwind )
158            do_libunwind="no"
159            ;;
160        -no-test-suite )
161            do_test_suite="no"
162            ;;
163        -no-openmp )
164            do_openmp="no"
165            ;;
166        -no-lld )
167            do_lld="no"
168            ;;
169        -lldb )
170            do_lldb="yes"
171            ;;
172        -no-lldb )
173            do_lldb="no"
174            ;;
175        -no-polly )
176            do_polly="no"
177            ;;
178        -no-mlir )
179            do_mlir="no"
180            ;;
181        -no-flang )
182            do_flang="no"
183            ;;
184        -silent-log )
185            do_silent_log="yes"
186            ;;
187        -help | --help | -h | --h | -\? )
188            usage
189            exit 0
190            ;;
191        * )
192            echo "unknown option: $1"
193            usage
194            exit 1
195            ;;
196    esac
197    shift
198done
199
200if [ $do_mlir = "no" ] && [ $do_flang = "yes" ]; then
201  echo "error: cannot build Flang without MLIR"
202  exit 1
203fi
204
205# Check required arguments.
206if [ -z "$Release" ]; then
207    echo "error: no release number specified"
208    exit 1
209fi
210if [ -z "$RC" ]; then
211    echo "error: no release candidate number specified"
212    exit 1
213fi
214if [ -z "$ExportBranch" ]; then
215    ExportBranch="tags/RELEASE_$Release_no_dot/$RC"
216fi
217if [ -z "$Triple" ]; then
218    echo "error: no target triple specified"
219    exit 1
220fi
221
222if [ "$Release" != "test" ]; then
223  if [ -n "$git_ref" ]; then
224    echo "error: can't specify both -release and -git-ref"
225    exit 1
226  fi
227  git_ref=llvmorg-$Release
228  if [ "$RC" != "final" ]; then
229    git_ref="$git_ref-$RC"
230  fi
231fi
232
233# Figure out how many make processes to run.
234if [ -z "$NumJobs" ]; then
235    NumJobs=`sysctl -n hw.activecpu 2> /dev/null || true`
236fi
237if [ -z "$NumJobs" ]; then
238    NumJobs=`sysctl -n hw.ncpu 2> /dev/null || true`
239fi
240if [ -z "$NumJobs" ]; then
241    NumJobs=`grep -c processor /proc/cpuinfo 2> /dev/null || true`
242fi
243if [ -z "$NumJobs" ]; then
244    NumJobs=3
245fi
246
247# Projects list
248projects="llvm clang"
249if [ $do_clang_tools = "yes" ]; then
250  projects="$projects clang-tools-extra"
251fi
252runtimes=""
253if [ $do_rt = "yes" ]; then
254  runtimes="$runtimes compiler-rt"
255fi
256if [ $do_libs = "yes" ]; then
257  runtimes="$runtimes libcxx"
258  if [ $do_libcxxabi = "yes" ]; then
259    runtimes="$runtimes libcxxabi"
260  fi
261  if [ $do_libunwind = "yes" ]; then
262    runtimes="$runtimes libunwind"
263  fi
264fi
265if [ $do_openmp = "yes" ]; then
266  projects="$projects openmp"
267fi
268if [ $do_lld = "yes" ]; then
269  projects="$projects lld"
270fi
271if [ $do_lldb = "yes" ]; then
272  projects="$projects lldb"
273fi
274if [ $do_polly = "yes" ]; then
275  projects="$projects polly"
276fi
277if [ $do_mlir = "yes" ]; then
278  projects="$projects mlir"
279fi
280if [ $do_flang = "yes" ]; then
281  projects="$projects flang"
282fi
283
284# Go to the build directory (may be different from CWD)
285BuildDir=$BuildDir/$RC
286mkdir -p $BuildDir
287cd $BuildDir
288
289# Location of log files.
290LogDir=$BuildDir/logs
291mkdir -p $LogDir
292
293# Final package name.
294Package=clang+llvm-$Release
295if [ $RC != "final" ]; then
296  Package=$Package-$RC
297fi
298Package=$Package-$Triple
299
300# Errors to be highlighted at the end are written to this file.
301echo -n > $LogDir/deferred_errors.log
302
303function deferred_error() {
304  Phase="$1"
305  Flavor="$2"
306  Msg="$3"
307  echo "[${Flavor} Phase${Phase}] ${Msg}" | tee -a $LogDir/deferred_errors.log
308}
309
310# Make sure that a required program is available
311function check_program_exists() {
312  local program="$1"
313  if ! type -P $program > /dev/null 2>&1 ; then
314    echo "program '$1' not found !"
315    exit 1
316  fi
317}
318
319if [ "$System" != "Darwin" ] && [ "$System" != "SunOS" ] && [ "$System" != "AIX" ]; then
320  check_program_exists 'chrpath'
321fi
322
323if [ "$System" != "Darwin" ]; then
324  check_program_exists 'file'
325  check_program_exists 'objdump'
326fi
327
328check_program_exists ${MAKE}
329
330# Export sources to the build directory.
331function export_sources() {
332  SrcDir=$BuildDir/llvm-project
333  mkdir -p $SrcDir
334  echo "# Using git ref: $git_ref"
335
336  # GitHub allows you to download a tarball of any commit using the URL:
337  # https://github.com/$organization/$repo/archive/$ref.tar.gz
338  curl -L https://github.com/llvm/llvm-project/archive/$git_ref.tar.gz | \
339    tar -C $SrcDir --strip-components=1 -xzf -
340
341  if [ "$do_test_suite" = "yes" ]; then
342    TestSuiteSrcDir=$BuildDir/llvm-test-suite
343    mkdir -p $TestSuiteSrcDir
344
345    # We can only use named refs, like branches and tags, that exist in
346    # both the llvm-project and test-suite repos if we want to run the
347    # test suite.
348    # If the test-suite fails to download assume we are using a ref that
349    # doesn't exist in the test suite and disable it.
350    set +e
351    curl -L https://github.com/llvm/test-suite/archive/$git_ref.tar.gz | \
352      tar -C $TestSuiteSrcDir --strip-components=1 -xzf -
353    if [ $? -ne -0 ]; then
354      echo "$git_ref not found in test-suite repo, test-suite disabled."
355      do_test_suite="no"
356    fi
357    set -e
358  fi
359
360  cd $BuildDir
361}
362
363function configure_llvmCore() {
364    Phase="$1"
365    Flavor="$2"
366    ObjDir="$3"
367
368    case $Flavor in
369        Release )
370            BuildType="Release"
371            Assertions="OFF"
372            ;;
373        Release+Asserts )
374            BuildType="Release"
375            Assertions="ON"
376            ;;
377        Debug )
378            BuildType="Debug"
379            Assertions="ON"
380            ;;
381        * )
382            echo "# Invalid flavor '$Flavor'"
383            echo ""
384            return
385            ;;
386    esac
387
388    project_list=${projects// /;}
389    runtime_list=${runtimes// /;}
390    echo "# Using C compiler: $c_compiler"
391    echo "# Using C++ compiler: $cxx_compiler"
392
393    cd $ObjDir
394    echo "# Configuring llvm $Release-$RC $Flavor"
395
396    echo "#" env CC="$c_compiler" CXX="$cxx_compiler" \
397        cmake -G "$generator" \
398        -DCMAKE_BUILD_TYPE=$BuildType -DLLVM_ENABLE_ASSERTIONS=$Assertions \
399        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
400        -DLLVM_ENABLE_PROJECTS="$project_list" \
401        -DLLVM_LIT_ARGS="-j $NumJobs" \
402        -DLLVM_ENABLE_RUNTIMES="$runtime_list" \
403        $ExtraConfigureFlags $BuildDir/llvm-project/llvm \
404        2>&1 | tee $LogDir/llvm.configure-Phase$Phase-$Flavor.log
405    env CC="$c_compiler" CXX="$cxx_compiler" \
406        cmake -G "$generator" \
407        -DCMAKE_BUILD_TYPE=$BuildType -DLLVM_ENABLE_ASSERTIONS=$Assertions \
408        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
409        -DLLVM_ENABLE_PROJECTS="$project_list" \
410        -DLLVM_LIT_ARGS="-j $NumJobs" \
411        -DLLVM_ENABLE_RUNTIMES="$runtime_list" \
412        $ExtraConfigureFlags $BuildDir/llvm-project/llvm \
413        2>&1 | tee $LogDir/llvm.configure-Phase$Phase-$Flavor.log
414
415    cd $BuildDir
416}
417
418function build_llvmCore() {
419    Phase="$1"
420    Flavor="$2"
421    ObjDir="$3"
422    DestDir="$4"
423
424    Verbose="VERBOSE=1"
425    if [ ${MAKE} = 'ninja' ]; then
426      Verbose="-v"
427    fi
428
429    redir="/dev/stdout"
430    if [ $do_silent_log == "yes" ]; then
431      echo "# Silencing build logs because of -silent-log flag..."
432      redir="/dev/null"
433    fi
434
435    cd $ObjDir
436    echo "# Compiling llvm $Release-$RC $Flavor"
437    echo "# ${MAKE} -j $NumJobs $Verbose"
438    ${MAKE} -j $NumJobs $Verbose \
439        2>&1 | tee $LogDir/llvm.make-Phase$Phase-$Flavor.log > $redir
440
441    echo "# Installing llvm $Release-$RC $Flavor"
442    echo "# ${MAKE} install"
443    DESTDIR="${DestDir}" ${MAKE} install \
444        2>&1 | tee $LogDir/llvm.install-Phase$Phase-$Flavor.log > $redir
445    cd $BuildDir
446}
447
448function test_llvmCore() {
449    Phase="$1"
450    Flavor="$2"
451    ObjDir="$3"
452
453    KeepGoing="-k"
454    if [ ${MAKE} = 'ninja' ]; then
455      # Ninja doesn't have a documented "keep-going-forever" mode, we need to
456      # set a limit on how many jobs can fail before we give up.
457      KeepGoing="-k 100"
458    fi
459
460    cd $ObjDir
461    if ! ( ${MAKE} -j $NumJobs $KeepGoing check-all \
462        2>&1 | tee $LogDir/llvm.check-Phase$Phase-$Flavor.log ) ; then
463      deferred_error $Phase $Flavor "check-all failed"
464    fi
465
466    if [ $do_test_suite = 'yes' ]; then
467      cd $TestSuiteBuildDir
468      env CC="$c_compiler" CXX="$cxx_compiler" \
469          cmake $TestSuiteSrcDir -G "$generator" -DTEST_SUITE_LIT=$Lit \
470                -DTEST_SUITE_HOST_CC=$build_compiler
471
472      if ! ( ${MAKE} -j $NumJobs $KeepGoing check \
473          2>&1 | tee $LogDir/llvm.check-Phase$Phase-$Flavor.log ) ; then
474        deferred_error $Phase $Flavor "test suite failed"
475      fi
476    fi
477    cd $BuildDir
478}
479
480# Clean RPATH. Libtool adds the build directory to the search path, which is
481# not necessary --- and even harmful --- for the binary packages we release.
482function clean_RPATH() {
483  if [ "$System" = "Darwin" ] || [ "$System" = "SunOS" ] || [ "$System" = "AIX" ]; then
484    return
485  fi
486  local InstallPath="$1"
487  for Candidate in `find $InstallPath/{bin,lib} -type f`; do
488    if file $Candidate | grep ELF | egrep 'executable|shared object' > /dev/null 2>&1 ; then
489      if rpath=`objdump -x $Candidate | grep 'RPATH'` ; then
490        rpath=`echo $rpath | sed -e's/^ *RPATH *//'`
491        if [ -n "$rpath" ]; then
492          newrpath=`echo $rpath | sed -e's/.*\(\$ORIGIN[^:]*\).*/\1/'`
493          chrpath -r $newrpath $Candidate 2>&1 > /dev/null 2>&1
494        fi
495      fi
496    fi
497  done
498}
499
500# Create a package of the release binaries.
501function package_release() {
502    cwd=`pwd`
503    cd $BuildDir/Phase3/Release
504    mv llvmCore-$Release-$RC.install/usr/local $Package
505    if [ "$use_gzip" = "yes" ]; then
506      tar cf - $Package | gzip -9c > $BuildDir/$Package.tar.gz
507    else
508      tar cf - $Package | xz -9ce > $BuildDir/$Package.tar.xz
509    fi
510    mv $Package llvmCore-$Release-$RC.install/usr/local
511    cd $cwd
512}
513
514# Exit if any command fails
515# Note: pipefail is necessary for running build commands through
516# a pipe (i.e. it changes the output of ``false | tee /dev/null ; echo $?``)
517set -e
518set -o pipefail
519
520# Turn off core dumps, as some test cases can easily fill up even the largest
521# file systems.
522ulimit -c 0
523
524if [ "$do_checkout" = "yes" ]; then
525    export_sources
526fi
527
528# Setup the test-suite.  Do this early so we can catch failures before
529# we do the full 3 stage build.
530if [ $do_test_suite = "yes" ]; then
531  check_program_exists 'python3'
532  venv="python3 -m venv"
533
534  SandboxDir="$BuildDir/sandbox"
535  Lit=$SandboxDir/bin/lit
536  TestSuiteBuildDir="$BuildDir/test-suite-build"
537  TestSuiteSrcDir="$BuildDir/llvm-test-suite"
538
539  ${venv} $SandboxDir
540  $SandboxDir/bin/python $BuildDir/llvm-project/llvm/utils/lit/setup.py install
541  mkdir -p $TestSuiteBuildDir
542fi
543
544(
545Flavors="Release"
546if [ "$do_debug" = "yes" ]; then
547    Flavors="Debug $Flavors"
548fi
549if [ "$do_asserts" = "yes" ]; then
550    Flavors="$Flavors Release+Asserts"
551fi
552
553for Flavor in $Flavors ; do
554    echo ""
555    echo ""
556    echo "********************************************************************************"
557    echo "  Release:     $Release-$RC"
558    echo "  Build:       $Flavor"
559    echo "  System Info: "
560    echo "    `uname -a`"
561    echo "********************************************************************************"
562    echo ""
563
564    c_compiler="$CC"
565    cxx_compiler="$CXX"
566    build_compiler="$CC"
567    [[ -z "$build_compiler" ]] && build_compiler="cc"
568    llvmCore_phase1_objdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-$RC.obj
569    llvmCore_phase1_destdir=$BuildDir/Phase1/$Flavor/llvmCore-$Release-$RC.install
570
571    llvmCore_phase2_objdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-$RC.obj
572    llvmCore_phase2_destdir=$BuildDir/Phase2/$Flavor/llvmCore-$Release-$RC.install
573
574    llvmCore_phase3_objdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-$RC.obj
575    llvmCore_phase3_destdir=$BuildDir/Phase3/$Flavor/llvmCore-$Release-$RC.install
576
577    rm -rf $llvmCore_phase1_objdir
578    rm -rf $llvmCore_phase1_destdir
579
580    rm -rf $llvmCore_phase2_objdir
581    rm -rf $llvmCore_phase2_destdir
582
583    rm -rf $llvmCore_phase3_objdir
584    rm -rf $llvmCore_phase3_destdir
585
586    mkdir -p $llvmCore_phase1_objdir
587    mkdir -p $llvmCore_phase1_destdir
588
589    mkdir -p $llvmCore_phase2_objdir
590    mkdir -p $llvmCore_phase2_destdir
591
592    mkdir -p $llvmCore_phase3_objdir
593    mkdir -p $llvmCore_phase3_destdir
594
595    ############################################################################
596    # Phase 1: Build llvmCore and clang
597    echo "# Phase 1: Building llvmCore"
598    configure_llvmCore 1 $Flavor $llvmCore_phase1_objdir
599    build_llvmCore 1 $Flavor \
600        $llvmCore_phase1_objdir $llvmCore_phase1_destdir
601    clean_RPATH $llvmCore_phase1_destdir/usr/local
602
603    ########################################################################
604    # Phase 2: Build llvmCore with newly built clang from phase 1.
605    c_compiler=$llvmCore_phase1_destdir/usr/local/bin/clang
606    cxx_compiler=$llvmCore_phase1_destdir/usr/local/bin/clang++
607    echo "# Phase 2: Building llvmCore"
608    configure_llvmCore 2 $Flavor $llvmCore_phase2_objdir
609    build_llvmCore 2 $Flavor \
610        $llvmCore_phase2_objdir $llvmCore_phase2_destdir
611    clean_RPATH $llvmCore_phase2_destdir/usr/local
612
613    ########################################################################
614    # Phase 3: Build llvmCore with newly built clang from phase 2.
615    c_compiler=$llvmCore_phase2_destdir/usr/local/bin/clang
616    cxx_compiler=$llvmCore_phase2_destdir/usr/local/bin/clang++
617    echo "# Phase 3: Building llvmCore"
618    configure_llvmCore 3 $Flavor $llvmCore_phase3_objdir
619    build_llvmCore 3 $Flavor \
620        $llvmCore_phase3_objdir $llvmCore_phase3_destdir
621    clean_RPATH $llvmCore_phase3_destdir/usr/local
622
623    ########################################################################
624    # Testing: Test phase 3
625    c_compiler=$llvmCore_phase3_destdir/usr/local/bin/clang
626    cxx_compiler=$llvmCore_phase3_destdir/usr/local/bin/clang++
627    echo "# Testing - built with clang"
628    test_llvmCore 3 $Flavor $llvmCore_phase3_objdir
629
630    ########################################################################
631    # Compare .o files between Phase2 and Phase3 and report which ones
632    # differ.
633    if [ "$do_compare" = "yes" ]; then
634        echo
635        echo "# Comparing Phase 2 and Phase 3 files"
636        for p2 in `find $llvmCore_phase2_objdir -name '*.o'` ; do
637            p3=`echo $p2 | sed -e 's,Phase2,Phase3,'`
638            # Substitute 'Phase2' for 'Phase3' in the Phase 2 object file in
639            # case there are build paths in the debug info. Do the same sub-
640            # stitution on both files in case the string occurrs naturally.
641            if ! cmp -s \
642                <(env LC_CTYPE=C sed -e 's,Phase1,Phase2,g' -e 's,Phase2,Phase3,g' $p2) \
643                <(env LC_CTYPE=C sed -e 's,Phase1,Phase2,g' -e 's,Phase2,Phase3,g' $p3) \
644                16 16; then
645                echo "file `basename $p2` differs between phase 2 and phase 3"
646            fi
647        done
648    fi
649done
650
651) 2>&1 | tee $LogDir/testing.$Release-$RC.log
652
653if [ "$use_gzip" = "yes" ]; then
654  echo "# Packaging the release as $Package.tar.gz"
655else
656  echo "# Packaging the release as $Package.tar.xz"
657fi
658package_release
659
660set +e
661
662# Woo hoo!
663echo "### Testing Finished ###"
664echo "### Logs: $LogDir"
665
666echo "### Errors:"
667if [ -s "$LogDir/deferred_errors.log" ]; then
668  cat "$LogDir/deferred_errors.log"
669  exit 1
670else
671  echo "None."
672fi
673
674exit 0
675