From 0263378acb51d083bf573c7a3b12220d0794f68f Mon Sep 17 00:00:00 2001 From: LogicParrot Date: Thu, 4 Aug 2016 18:37:47 +0300 Subject: compile.sh rewrite. Commandline arguments and more. (#3255) --- compile.sh | 493 +++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 363 insertions(+), 130 deletions(-) diff --git a/compile.sh b/compile.sh index 166edea54..0f26f8607 100755 --- a/compile.sh +++ b/compile.sh @@ -3,39 +3,183 @@ { # put the whole thing in a block so as not to behave weirdly if interrupted set -e -#TODO command line parameter handling for non-interactive mode. +# Global variables: +# CHOICE_BUILDTYPE - Either "Normal" or "Debug". +# CHOICE_THREADS - A numerical value, the amount of threads to be used for the make command. +# CHOICE_BRANCH - The branch to use. Currently locked on "master". +# STATE_INTERACTIVE - 1 If we're running interactively. 0 otherwise. +# STATE_NEW - Whether this is the first run. If 1, then no GIT repo exists yet. 0 otherwise. -# Do we already have a repo? -if [ -d .git ] && [ -f easyinstall.sh ] && [ -f src/BlockArea.cpp ]; then # A good enough indicator that we're in the Cuberite git repo. - cd ../ - echo "Cuberite repository detected. This should make the process faster, especially if you compiled before." -fi +# Constants: +DEFAULT_BUILDTYPE="Release" # Other options: "Debug" +DEFAULT_BRANCH="master" # Other options: None currently +DEFAULT_THREADS=2 + +# Constants not modifiable through command line: +UPSTREAM_REPO="origin" +UPSTREAM_LINK="https://github.com/cuberite/cuberite.git" +DRY_RUN="no" + +#=================== Error functions =================== -# Error functions. -error () + +errorCompile () { echo - echo "-----------------" - echo "Script aborted, reason:" + echoInt "-----------------" + echo "Compilation failed. Failed command:" echo "$@" exit 1 } -missingDepsExit () +errorGit () { - if [ "$1" != "" ]; then - echo "You can install the missing depndencies via:" - echo "$1" - fi - echo - echo "Please install the dependencies, then come back." echo + echoInt "-----------------" + echo "Code fetch failed. (Check your network connection). Failed command:" + echo "$@" exit 2 } +errorDependencies () +{ + # The error messages are complex and OS-dependant, and are printed in the dependencies section before this is called. + exit 3 +} + +errorArguments () +{ + echo "Usage: ./compile.sh [options]" + echo "Compiles Cuberite. Updates the GIT repository if needed, and downloads it if it does not exist." + echo "Runs interactively, unless one or more options are specified." + echo + echo "options:" + echo " -m The compilation mode. Either \"Release\" or \"Debug\". Defaults to \"$DEFAULT_BUILDTYPE\"" + echo ' -t The number of threads to use for compiling' + echo ' If unspecified, a "smart guess" is attempted (Currently simply picks 2)' + echo ' -b The branch to compile. (Currently unused and pinned to MASTER)' + echo ' -n Prevent interactive mode' + echo ' -d Dry run. Print the chosen settings and exit' + echo + echo "Usage examples:" + echo " ./compile.sh" + echo " ./compile.sh -m Debug" + echo " ./compile.sh -m Release -t 2" + echo + echo "Return codes: (non 0 returns are accompanied by useful stderr info)" + echo "0 - Success - Success! Code was updated and compiled" + echo "1 - Compilation failed - cmake, make, or source code issue" + echo "2 - Code fetch failed - Network issue or, far more rarely, a git issue" + echo "3 - Dependencies missing - Some compilation tools are missing" + echo "4 - Bad arguments - Bad commandline arguments were passed" + echo "5 - Bad user input - Invalid user input in interactive mode" + echo "6 - other - An error not listed above" + exit 4 +} + +errorInput () +{ + echo + echoInt "-----------------" + echo "Unrecognized user input" + echo "$@" + exit 5 +} + +errorOther () +{ + echo + echoInt "-----------------" + echo "$@" + exit 6 +} + + +#=================== Echo functions =================== + + +echoInt () # echo only if interactive mode. +{ + if [ $STATE_INTERACTIVE -eq 1 ]; then + echo "$1" + fi +} + +echoErr () # Echo to stderr. +{ + echo "$1" 1>&2 +} + + +#=================== Commandline Parsing =================== + + +STATE_INTERACTIVE=1 # Interactive, unless one or more command line options are passed. +while getopts ":m:t:b:" name; do + value=$OPTARG + STATE_INTERACTIVE=0 + case "$name" in + m) + if [ ! -z "$CHOICE_BUILDTYPE" ]; then errorArguments; fi # Argument duplication. + if [ "$value" = "Debug" ] || [ "$value" = "Release" ]; then + CHOICE_BUILDTYPE="$value" + else + errorArguments + fi + ;; + t) + if [ ! -z "$CHOICE_THREADS" ]; then errorArguments; fi # Argument duplication. + if [ "$value" -gt 0 ] 2>/dev/null; then # If a positive integer. + CHOICE_THREADS="$value" + else + errorArguments + fi + ;; + b) + if [ ! -z "$CHOICE_BRANCH" ]; then errorArguments; fi # Argument duplication. + CHOICE_BRANCH=1 # Only used for dupe checking, overridden below. + echoErr "Warning: The -b option is currently unused, it was ignored" + ;; + n) + if [ "$dummy" = "1" ]; then errorArguments; fi # Argument duplication. + dummy=1 + ;; + *) + errorArguments + ;; + esac +done + + +#=================== Dependency checks and greeting =================== + + +# Do we already have a repo? +checkCuberiteDir () +{ + [ -d .git ] && [ -f easyinstall.sh ] && [ -f src/BlockArea.cpp ] # A good enough indicator that we're in the Cuberite git repo. +} + +STATE_NEW=1 +if checkCuberiteDir; then # Check if we're in the Cuberite directory... + STATE_NEW=0 +elif [ -d cuberite ]; then # If there's a directory named "cuberite"... + cd cuberite + if checkCuberiteDir; then # Check if we're in the Cuberite directory... + STATE_NEW=0 + else + errorOther "A directory is named 'cuberite' which has no Cuberite assets exists. Please run the script elsewhere or move/delete that directory." + fi + +fi + +if [ $STATE_NEW -eq 0 ]; then + echoInt "Cuberite repository detected. This should make the process faster, especially if you compiled before." +fi # Echo: Greetings. -echo " +echoInt " + Hello, this script will download and compile Cuberite. On subsequent runs, it will update Cuberite. The compilation and download will occur in the current directory. @@ -44,84 +188,102 @@ Compiling from source takes time, but it usually generates faster executables. If you prefer ready-to-use binaries or if you want more info, please visit: http://cuberite.org/" -### Dependency checks start. ### -MISSING_PACKAGES="" +doDependencyCheck() +{ + MISSING_PACKAGES="" -# Most distros have the following default package and executable names. -GCC_EXE_NAME="g++" -CLANG_EXE_NAME="clang" -COMPILER_PACKAGE_NAME="gcc g++" + # Most distros have the following default compiler names. + GCC_EXE_NAME="g++" + CLANG_EXE_NAME="clang" + COMPILER_PACKAGE_NAME="gcc g++" -# Left side: Executable Name, Right side: Package Name. Note that this is TAB delimited. Spaces will not work. -PROGRAMS='git git -make make -cmake cmake' + # Most distros have the following package and executable names. + # Left side: Executable Name, Right side: Package Name. Note that this is SPACE delimited now, unlike in the past. + PROGRAMS='git git + make make + cmake cmake' -# If any OS deviates from the defaults, detect the OS here, and change PROGRAMS, COMPILER_PACKAGE_NAME, etc. as needed. + # If any OS deviates from the defaults, we detect the OS here, and change PROGRAMS, COMPILER_PACKAGE_NAME, etc. as needed. -# Fedora, CentOS, RHEL, Mageia, openSUSE, Mandriva -if (rpm --help > /dev/null 2> /dev/null); then - COMPILER_PACKAGE_NAME="gcc-c++" -fi + # Fedora, CentOS, RHEL, Mageia, openSUSE, Mandriva. + if (rpm --help > /dev/null 2> /dev/null); then + COMPILER_PACKAGE_NAME="gcc-c++" + fi -# Compiler check. -GCC_EXISTS=0 -CLANG_EXISTS=0 -$GCC_EXE_NAME --help > /dev/null 2> /dev/null && GCC_EXISTS=1 -$CLANG_EXE_NAME --help > /dev/null 2> /dev/null && CLANG_EXISTS=1 -if [ "$GCC_EXISTS" -eq 0 ] && [ "$CLANG_EXISTS" -eq 0 ]; then - MISSING_PACKAGES=" $COMPILER_PACKAGE_NAME" -fi + # Make sure at least one compiler exists. + GCC_EXISTS=0 + CLANG_EXISTS=0 + $GCC_EXE_NAME --help > /dev/null 2> /dev/null && GCC_EXISTS=1 + $CLANG_EXE_NAME --help > /dev/null 2> /dev/null && CLANG_EXISTS=1 + if [ "$GCC_EXISTS" -eq 0 ] && [ "$CLANG_EXISTS" -eq 0 ]; then + MISSING_PACKAGES=" $COMPILER_PACKAGE_NAME" + fi -# Depdendency check. -checkPackages () -{ - # note that IFS is a TAB here! - echo "$PROGRAMS" | while IFS=' ' read EXE_NAME PACKAGE_NAME __trash__; do - command -v $EXE_NAME > /dev/null 2> /dev/null || printf %s " $PACKAGE_NAME" - done + # Depdendency check. + checkPackages () + { + echo "$PROGRAMS" | while read line; do + EXE_NAME=`echo "$line" | cut -f 1 -d " "` + PACKAGE_NAME=`echo "$line" | cut -f 2 -d " "` + command -v $EXE_NAME > /dev/null 2> /dev/null || printf %s " $PACKAGE_NAME" + done + } + MISSING_PACKAGES="$MISSING_PACKAGES`checkPackages`" + missingDepsExit () + { + if [ "$1" != "" ]; then + echoErr "You can install the missing depndencies via:" + echoErr "$1" + fi + echoErr + echoErr "Please install the dependencies, then come back." + echoErr + errorDependencies + } + + if [ "$MISSING_PACKAGES" != "" ]; then + echoInt + echoInt "-----------------" + echoErr "You have missing compilation dependencies:" + echoErr $MISSING_PACKAGES + echoErr + + # apt-get guide. + apt-get --help > /dev/null 2> /dev/null && \ + missingDepsExit "sudo apt-get install$MISSING_PACKAGES" + + # yum guide. + yum --help > /dev/null 2> /dev/null && \ + missingDepsExit "sudo yum install$MISSING_PACKAGES" + + # zypper guide. + zypper --help > /dev/null 2> /dev/null && \ + missingDepsExit "sudo zypper install$MISSING_PACKAGES" + + # pacman guide. + pacman --help > /dev/null 2> /dev/null && \ + missingDepsExit "sudo pacman -S$MISSING_PACKAGES" + + # urpmi guide. + urpmi --help > /dev/null 2> /dev/null && \ + missingDepsExit "sudo urpmi$MISSING_PACKAGES" + + missingDepsExit "" + fi } -MISSING_PACKAGES="$MISSING_PACKAGES`checkPackages`" - -if [ "$MISSING_PACKAGES" != "" ]; then - echo - echo "-----------------" - echo "You have missing compilation dependencies:" - echo $MISSING_PACKAGES - echo - - # apt-get guide. - apt-get --help > /dev/null 2> /dev/null && \ - missingDepsExit "sudo apt-get install $MISSING_PACKAGES" - - # yum guide. - yum --help > /dev/null 2> /dev/null && \ - missingDepsExit "sudo yum install $MISSING_PACKAGES" +doDependencyCheck - # zypper guide. - zypper --help > /dev/null 2> /dev/null && \ - missingDepsExit "sudo zypper install $MISSING_PACKAGES" - # pacman guide. - pacman --help > /dev/null 2> /dev/null && \ - missingDepsExit "sudo pacman -S $MISSING_PACKAGES" +#=================== Choice: Branch (Currently unused and simply skipped) =================== - # urpmi guide. - urpmi --help > /dev/null 2> /dev/null && \ - missingDepsExit "sudo urpmi $MISSING_PACKAGES" - - missingDepsExit "" -fi -### Dependency checks end. ### # Bypass Branch choice and choose master. Because it's the only branch right now. -BRANCH="master" +CHOICE_BRANCH=$DEFAULT_BRANCH ### Inactive code start. ### inactiveCode () { -# Echo: Branch choice. echo " You can choose between 3 branches: * (S)Stable: Choose the stable branch if you want the most @@ -136,75 +298,135 @@ You can choose between 3 branches: " -# Input: Branch choice. printf %s "Choose the branch (s/t/d): " -read BRANCH -case $BRANCH in +read CHOICE_BRANCH +case $CHOICE_BRANCH in s|S) - error "We don't have a stable branch yet, please use testing, sorry." + errorOther "We don't have a stable branch yet, please use testing, sorry." ;; t|T) - BRANCH="testing" + CHOICE_BRANCH="testing" ;; d|D) - BRANCH="master" + CHOICE_BRANCH="master" ;; *) - error "Unrecognized user input." + errorInput ;; esac } ### Inactive code end. ### -# Echo: Compile mode choice. -echo " -Choose compile mode: -* (N)Normal: Compiles normally. - Generates the fastest build. - -* (D)Debug: Compiles in debug mode. - Makes your console and crashes more verbose. - A bit slower than Normal mode. If you plan to help - development by reporting bugs, this is preferred. - - -Note that the script will connect to the internet in order to fetch -code after this step. It will then compile your program. -" -# Input: Compile mode choice. -printf %s "Choose compile mode: (n/d): " -read BUILDTYPE -case $BUILDTYPE in - d|D) - BUILDTYPE="Debug" - ;; - n|N) - BUILDTYPE="Release" - ;; - *) - error "Unrecognized user input." - ;; -esac +#=================== Choice: Compile mode =================== + + +if [ $STATE_INTERACTIVE -eq 1 ]; then + echo " + Choose compile mode: + * (R)Release: Compiles normally. + Generates the fastest build. + + * (D)Debug: Compiles in debug mode. + Makes your console and crashes more verbose. + A bit slower than Release mode. If you plan to help + development by reporting bugs, this is preferred. + + " + + printf %s "Choose compile mode: (r/d) (Default: \"$DEFAULT_BUILDTYPE\"): " + read CHOICE_BUILDTYPE + case $CHOICE_BUILDTYPE in + d|D) + CHOICE_BUILDTYPE="Debug" + ;; + r|N) + CHOICE_BUILDTYPE="Release" + ;; + ""|N) + CHOICE_BUILDTYPE="$DEFAULT_BUILDTYPE" + ;; + *) + errorInput + ;; + esac +elif [ -z "$CHOICE_BUILDTYPE" ]; then + CHOICE_BUILDTYPE="$DEFAULT_BUILDTYPE" # Non interactive mode with no buildtype specified. +fi -# Echo: Downloading began. -echo -echo " --- Downloading Cuberite's source code from the $BRANCH branch..." +#=================== Choice: Thread amount =================== -if [ ! -d cuberite ]; then + +autoChooseThreads() +{ + echo "$DEFAULT_THREADS" # Todo choose based on system info, and fallback to DEFAULT_THREADS. +} + +if [ $STATE_INTERACTIVE -eq 1 ]; then + printf %s "Enter the number of threads to use. Leave empty for an automatic choice: " + read CHOICE_THREADS + if [ "$CHOICE_THREADS" = "" ] 2> /dev/null; then + CHOICE_THREADS=`autoChooseThreads` + elif [ "$CHOICE_THREADS" -lt 0 ] 2> /dev/null; then + errorInput + fi + +elif [ -z "$CHOICE_THREADS" ]; then . + CHOICE_THREADS=`autoChooseThreads` # Non interactive mode with no thread amount specified. +fi + + +#=================== Print settings summary =================== + + +if [ "$STATE_NEW" = 1 ]; then + previousCompilation="Not detected. We are assuming this is the first compile.sh run." +else + previousCompilation="Detected. This should make fetching and compiling faster." +fi + +# Ask the user's permission to connect to the net. +echo "" +echoInt "#### Settings Summary ####" +echo "Build Type: " "$CHOICE_BUILDTYPE" +echo "Branch: " "$CHOICE_BRANCH" "(Currently the only choice)" +echo "Compilation threads: " "$CHOICE_THREADS" +echo "Previous compilation: " "$previousCompilation" +echo "Upstream Link: " "$UPSTREAM_LINK" +echo "Upstream Repo: " "$UPSTREAM_REPO" + +if [ "$DRY_RUN" = "yes" ]; then exit 0; fi + +if [ $STATE_INTERACTIVE -eq 1 ]; then + echo + echo "After pressing ENTER, the script will connect to $UPSTREAM_LINK" + echo "to check for updates and/or fetch code. It will then compile your program." + echo "If you compiled before, make sure you're in the proper directory and that \"Previous compilation\" is detected." + printf $s "Press ENTER to continue... " + read dummy +fi + + +#=================== Code download / update via git =================== + + +echoInt +echoInt " --- Downloading Cuberite's source code from the $CHOICE_BRANCH branch..." + + +if [ $STATE_NEW -eq 1 ]; then # Git: Clone. echo " --- Looks like your first run, cloning the whole code..." - git clone --depth 1 https://github.com/cuberite/cuberite.git -b "$BRANCH" + git clone --depth 1 "$UPSTREAM_LINK" -b "$CHOICE_BRANCH" || errorGit "git clone --depth 1 $UPSTREAM_LINK -b $CHOICE_BRANCH" cd cuberite else # Git: Fetch. - cd cuberite - echo " --- Updating the $BRANCH branch..." - git fetch origin "$BRANCH" || error "git fetch failed" - git checkout "$BRANCH" || error "git checkout failed" - git merge "origin/$BRANCH" || error "git merge failed" + echo " --- Updating the $CHOICE_BRANCH branch..." + git fetch "$UPSTREAM_REPO" "$CHOICE_BRANCH" || errorGit "git fetch $UPSTREAM_REPO $CHOICE_BRANCH" + git checkout "$CHOICE_BRANCH" || errorGit "git checkout $CHOICE_BRANCH" + git merge "$UPSTREAM_REPO"/"$CHOICE_BRANCH" || errorGit "git merge $UPSTREAM_REPO/$CHOICE_BRANCH" fi # Git: Submodules. @@ -212,31 +434,38 @@ echo " --- Updating submodules..." git submodule update --init +#=================== Compilation via cmake and make =================== + + # Cmake. echo " --- Running cmake..." if [ ! -d build-cuberite ]; then mkdir build-cuberite; fi cd build-cuberite -cmake .. -DCMAKE_BUILD_TYPE="$BUILDTYPE" || error "cmake failed" +cmake .. -DCMAKE_BUILD_TYPE="$CHOICE_BUILDTYPE" || errorCompile "cmake .. -DCMAKE_BUILD_TYPE=$CHOICE_BUILDTYPE" # Make. echo " --- Compiling..." -make -j 2 || error "Compiling failed" +make -j "$CHOICE_THREADS" || errorCompile "make -j $CHOICE_THREADS" echo -# Echo: Compilation complete. +#=================== Print success message =================== + + cd ../Server echo echo "-----------------" echo "Compilation done!" echo echo "Cuberite awaits you at:" -if [ "$BUILDTYPE" = "Debug" ]; then + +if [ "$CHOICE_BUILDTYPE" = "Debug" ]; then echo "$PWD/Cuberite_debug" else echo "$PWD/Cuberite" fi + cd .. echo " You can always update Cuberite by executing: @@ -245,9 +474,13 @@ You can always update Cuberite by executing: Enjoy :)" exit 0 + +#=================== Windows fallback =================== + + +# Called via hack in line 2. :windows_detected @echo off -rem Putting echo "Foo" in Windows CMD gives you "Foo" -- with quotes. echo This script is not available for Windows yet, sorry. echo You can still download the Windows binaries from: http://cuberite.org echo You can also manually compile for Windows. See: https://github.com/cuberite/cuberite -- cgit v1.2.3