3 # Programs returning != 0 will not cause script to exit
6 # Case insensitive string comparison
14 GIT_PROTOCOL=${GIT_PROTOCOL:="https"}
15 GIT_URL=${GIT_URL:=$GIT_PROTOCOL"://github.com/factor/factor.git"}
17 test_program_installed() {
18 command -v "$1" >/dev/null 2>&1
22 test_programs_installed() {
24 $ECHO -n "Checking for all($*)..."
27 if test_program_installed "$cmd"; then
31 if [[ $installed -eq $# ]] ; then
39 if [[ $FIND_MAKE_TARGET = true ]] ; then
46 ensure_program_installed() {
48 $ECHO -n "Checking for any($*)..."
51 if test_program_installed "$cmd"; then
59 if [[ $# -eq 1 ]] ; then
62 $ECHO -n "any of [ $* ]"
64 $ECHO " and try again."
65 if [[ $OS == macosx ]] ; then
66 $ECHO "If you have Xcode 4.3 or higher installed, you must install the"
67 $ECHO "Command Line Tools from Xcode Preferences > Downloads in order"
68 $ECHO "to build Factor."
74 # Can't execute any commands before saving $?
75 # $1 is the name of the command we are checking
77 if [[ $RET -ne 0 ]] ; then
83 download_with_wget() {
86 filename=$(basename "$url")
87 $ECHO filename is "$filename"
88 $ECHO wget -nd --prefer-family=IPv4 -O "$filename" "$url"
89 wget -nd --prefer-family=IPv4 -O "$filename" "$url"
92 download_with_curl() {
95 filename=$(basename "$url")
96 curl -L -f -o "$filename" "$url"
99 download_with_downloader() {
101 if [[ -z $DOWNLOADER_NAME ]] ; then
104 if [[ $DOWNLOADER_NAME == 'wget' ]]; then
105 download_with_wget "$url"
106 elif [[ $DOWNLOADER_NAME == 'curl' ]]; then
107 download_with_curl "$url"
109 $ECHO "error: wget or curl required in download_with_downloader"
115 if test_program_installed wget; then
119 if test_program_installed curl; then
123 $ECHO "error: wget or curl required"
128 if test_program_installed md5sum; then
136 # on Cygwin we MUST use the MinGW "cross-compiler", therefore check these first
137 # furthermore, we prefer 64 bit over 32 bit versions if both are available
139 # we need this condition so we don't find a mingw32 compiler on linux
140 if [[ $OS == windows ]] ; then
141 if test_programs_installed x86_64-w64-mingw32-gcc x86_64-w64-mingw32-g++; then
142 [ -z "$CC" ] && CC=x86_64-w64-mingw32-gcc
143 [ -z "$CXX" ] && CXX=x86_64-w64-mingw32-g++
147 if test_programs_installed i686-w64-mingw32-gcc i686-w64-mingw32-g++; then
148 [ -z "$CC" ] && CC=i686-w64-mingw32-gcc
149 [ -z "$CXX" ] && CXX=i686-w64-mingw32-g++
154 if test_programs_installed clang clang++ ; then
155 [ -z "$CC" ] && CC=clang
156 [ -z "$CXX" ] && CXX=clang++
160 # gcc and g++ will fail to correctly build Factor on Cygwin
161 if test_programs_installed gcc g++ ; then
162 [ -z "$CC" ] && CC=gcc
163 [ -z "$CXX" ] && CXX=g++
167 $ECHO "error: high enough version of either (clang/clang++) or (gcc/g++) required!"
173 freebsd) MAKE="gmake" ;;
176 if [[ $MAKE = "gmake" ]] ; then
177 ensure_program_installed gmake
181 check_installed_programs() {
182 ensure_program_installed chmod
183 ensure_program_installed uname
184 ensure_program_installed git
185 ensure_program_installed wget curl
186 ensure_program_installed clang x86_64-w64-mingw32-gcc i686-w64-mingw32-gcc gcc
187 ensure_program_installed clang++ x86_64-w64-mingw32-g++ i686-w64-mingw32-g++ g++ cl
188 ensure_program_installed make gmake
189 ensure_program_installed md5sum md5
190 ensure_program_installed cut
193 check_library_exists() {
194 GCC_TEST=factor-library-test.c
195 GCC_OUT=factor-library-test.out
196 $ECHO -n "Checking for library $1..."
197 $ECHO "int main(){return 0;}" > $GCC_TEST
198 if $CC $GCC_TEST -o $GCC_OUT -l "$1" 2>&- ; then
209 check_X11_libraries() {
210 check_library_exists GL
211 check_library_exists X11
212 check_library_exists pango-1.0
215 check_gtk_libraries() {
216 check_library_exists gobject-2.0
217 check_library_exists gtk-x11-2.0
218 check_library_exists gdk-x11-2.0
219 check_library_exists gdk_pixbuf-2.0
220 check_library_exists gtkglext-x11-1.0
221 check_library_exists atk-1.0
222 check_library_exists gio-2.0
223 check_library_exists gdkglext-x11-1.0
224 check_library_exists pango-1.0
230 linux) check_X11_libraries
231 check_gtk_libraries ;;
232 unix) check_gtk_libraries ;;
236 check_factor_exists() {
237 if [[ -d "factor" ]] ; then
238 $ECHO "A directory called 'factor' already exists."
239 $ECHO "Rename or delete it and try again."
245 if [[ -n $OS ]] ; then return; fi
246 $ECHO "Finding OS..."
251 CYGWIN_NT-5.2-WOW64) OS=windows ;;
252 *CYGWIN_NT*) OS=windows ;;
253 *CYGWIN*) OS=windows ;;
254 MINGW32*) OS=windows ;;
255 MINGW64*) OS=windows ;;
256 MSYS_NT*) OS=windows ;;
257 *darwin*) OS=macosx ;;
258 *Darwin*) OS=macosx ;;
261 FreeBSD) OS=freebsd ;;
266 find_architecture() {
267 if [[ -n $ARCH ]] ; then return; fi
268 $ECHO "Finding ARCH..."
281 iPhone5*[3-9]) ARCH=arm ;;
282 iPhone[6-9]*) ARCH=arm ;;
283 iPhone[1-9][0-9]*) ARCH=arm ;;
284 iPad[4-9]*) ARCH=arm ;;
285 iPad[1-9][0-9]*) ARCH=arm ;;
286 AppleTV[5-9]*) ARCH=arm ;;
287 AppleTV[1-9][0-9]*) ARCH=arm ;;
288 "Power Macintosh") ARCH=ppc ;;
293 $ECHO "Finding NUM_CORES..."
298 CYGWIN_NT-5.2-WOW64 | *CYGWIN_NT* | *CYGWIN* | MINGW32*) NUM_CORES=$NUMBER_OF_PROCESSORS ;;
299 *linux* | *Linux*) NUM_CORES=$(getconf _NPROCESSORS_ONLN || nproc) ;;
300 *darwin* | *Darwin* | freebsd) NUM_CORES=$(sysctl -n hw.ncpu) ;;
305 if [[ -n $WORD ]] ; then return; fi
306 $ECHO "Finding WORD..."
307 WORD=$(getconf LONG_BIT || find_word_size_cpp || find_word_size_c)
310 find_word_size_cpp() {
311 SIXTY_FOUR='defined(__aarch64__) || defined(__x86_64__) || defined(_M_AMD64) || defined(__PPC64__) || defined(__64BIT__)'
312 THIRTY_TWO='defined(i386) || defined(__i386) || defined(__i386__) || defined(_MIX86)'
313 $CC -E -xc <(echo -e "#if ${SIXTY_FOUR}\n64\n#elif ${THIRTY_TWO}\n32\n#endif") | tail -1
317 C_WORD="factor-word-size"
318 TEST_PROGRAM="int main(){ return (long)(8*sizeof(void*)); }"
319 echo "$TEST_PROGRAM" | $CC -o $C_WORD -xc -
327 echo "Word size should be 32/64, got '$WORD_OUT'"
334 set_factor_binary() {
336 windows) FACTOR_BINARY=factor.com ;;
337 *) FACTOR_BINARY=factor ;;
341 set_factor_library() {
343 windows) FACTOR_LIBRARY=factor.dll ;;
344 macosx) FACTOR_LIBRARY=libfactor.dylib ;;
345 *) FACTOR_LIBRARY=libfactor.a ;;
350 FACTOR_IMAGE=factor.image
351 FACTOR_IMAGE_FRESH=factor.image.fresh
357 $ECHO "NUM_CORES=$NUM_CORES"
360 $ECHO "REPRODUCIBLE=$REPRODUCIBLE"
361 $ECHO "CURRENT_BRANCH=$CURRENT_BRANCH"
362 $ECHO "CURRENT_BRANCH_FULL=$CURRENT_BRANCH_FULL"
363 $ECHO "FACTOR_BINARY=$FACTOR_BINARY"
364 $ECHO "FACTOR_LIBRARY=$FACTOR_LIBRARY"
365 $ECHO "FACTOR_IMAGE=$FACTOR_IMAGE"
366 $ECHO "MAKE_TARGET=$MAKE_TARGET"
367 $ECHO "BOOT_IMAGE=$BOOT_IMAGE"
368 $ECHO "MAKE_IMAGE_TARGET=$MAKE_IMAGE_TARGET"
369 $ECHO "GIT_PROTOCOL=$GIT_PROTOCOL"
370 $ECHO "GIT_URL=$GIT_URL"
371 $ECHO "CHECKSUM_URL=$CHECKSUM_URL"
372 $ECHO "BOOT_IMAGE_URL=$BOOT_IMAGE_URL"
373 $ECHO "DOWNLOADER_NAME=$DOWNLOADER_NAME"
379 check_os_arch_word() {
380 if ! [[ -n $OS && -n $ARCH && -n $WORD ]] ; then
384 $ECHO "OS, ARCH, or WORD is empty. Please report this."
393 if [[ $OS == "windows" ]] ; then
394 MAKE_IMAGE_TARGET=windows-$ARCH.$WORD
395 MAKE_TARGET=$OS-$ARCH-$WORD
397 MAKE_IMAGE_TARGET=unix-$ARCH.$WORD
398 MAKE_TARGET=$OS-$ARCH-$WORD
400 BOOT_IMAGE=boot.$MAKE_IMAGE_TARGET.image
404 ensure_program_installed cut
405 $ECHO "Parsing make target from command line: $1"
406 OS=$(echo "$1" | cut -d '-' -f 1)
407 ARCH=$(echo "$1" | cut -d '-' -f 2)
408 WORD=$(echo "$1" | cut -d '-' -f 3)
410 if [[ $OS == linux && $ARCH == ppc ]] ; then WORD=32; fi
411 if [[ $OS == linux && $ARCH == arm ]] ; then WORD=32; fi
412 if [[ $OS == macosx && $ARCH == ppc ]] ; then WORD=32; fi
419 prepare_build_info() {
446 $ECHO "Downloading the git repository from github.com..."
447 invoke_git clone "$GIT_URL"
450 update_script_name() {
451 $ECHO "$(dirname "$0")/_update.sh"
456 local -r update_script=$(update_script_name)
457 local -r shell_path="$SHELL"
461 echo "git pull ${GIT_URL} ${CURRENT_BRANCH}"
464 chmod 755 "$update_script"
465 $ECHO "Running the build.sh updater script: $update_script"
466 exec "$update_script"
469 update_script_changed() {
470 invoke_git diff --stat "$(invoke_git merge-base HEAD FETCH_HEAD)" FETCH_HEAD | grep "build.sh" >/dev/null
474 $ECHO "Fetching the git repository from github.com..."
477 rm -f "$(update_script_name)"
478 $ECHO git fetch "$GIT_URL" "${CURRENT_BRANCH}"
479 invoke_git fetch "$GIT_URL" "${CURRENT_BRANCH}"
481 if update_script_changed; then
482 $ECHO "Updating and restarting the build.sh script..."
485 $ECHO "Updating the working tree..."
486 invoke_git pull "$GIT_URL" "${CURRENT_BRANCH}"
491 cd "factor" || exit 12
496 $ECHO "Backing up factor..."
497 cp "$FACTOR_BINARY" "$FACTOR_BINARY.bak"
498 cp "$FACTOR_LIBRARY" "$FACTOR_LIBRARY.bak"
499 cp "$BOOT_IMAGE" "$BOOT_IMAGE.bak"
500 cp "$FACTOR_IMAGE" "$FACTOR_IMAGE.bak"
501 $ECHO "Done with backup."
504 check_makefile_exists() {
505 if [[ ! -e "GNUmakefile" ]] ; then
507 $ECHO "***GNUmakefile not found***"
508 $ECHO "You are likely in the wrong directory."
509 $ECHO "Run this script from your factor directory:"
516 check_makefile_exists
517 if [ -n "$MAKE_OPTS" ]; then
518 "$MAKE" "$MAKE_OPTS" "$@"
530 $ECHO "Building factor with $NUM_CORES cores"
531 invoke_make "CC=$CC" "CXX=$CXX" "$MAKE_TARGET" "-j$NUM_CORES"
534 make_clean_factor() {
539 current_git_branch() {
540 # git rev-parse --abbrev-ref HEAD # outputs HEAD for detached head
541 # outputs nothing for detached HEAD, which is fine for ``git fetch``
542 git describe --all --exact-match 2>/dev/null
547 if [[ $DOWNLOADER_NAME == 'wget' ]]; then
548 wget -S --spider --prefer-family=IPv4 "$1" 2>&1 | grep -q 'HTTP/[12].[01] [23]..' && return 0 || return 1
549 elif [[ $DOWNLOADER_NAME == 'curl' ]]; then
550 curl -4 -sL -w "%{http_code}\\n" "$1" -o /dev/null | grep -qE '^(2[0-9]{2})$' && return 0 || return 1
552 echo "error: wget or curl required to check URL"
557 # If we are on a branch, first try to get a boot image for that branch.
558 # Otherwise, just use `master`
559 set_boot_image_vars() {
561 local url="https://downloads.factorcode.org/images/${CURRENT_BRANCH}/checksums.txt"
562 $ECHO "Getting checksum from ${url}"
564 if check_url "$url"; then
565 $ECHO "got checksum!"
567 BOOT_IMAGE_URL="https://downloads.factorcode.org/images/${CURRENT_BRANCH}/${BOOT_IMAGE}"
569 $ECHO "boot image for branch \`${CURRENT_BRANCH}\` is not on server, trying master instead"
570 $ECHO " tried nonexistent url: ${url}"
571 CHECKSUM_URL="https://downloads.factorcode.org/images/master/checksums.txt"
572 BOOT_IMAGE_URL="https://downloads.factorcode.org/images/master/${BOOT_IMAGE}"
576 set_current_branch() {
577 if [ -n "${CI_BRANCH}" ]; then
578 CURRENT_BRANCH="${CI_BRANCH}"
580 CURRENT_BRANCH_FULL=$(current_git_branch)
581 CURRENT_BRANCH=$($ECHO "$CURRENT_BRANCH_FULL" | sed 's=heads/==;s=remotes/==')
585 update_boot_image() {
587 $ECHO "Deleting old images..."
588 rm -f checksums.txt* > /dev/null 2>&1
589 rm -f "$BOOT_IMAGE".{?,??} > /dev/null 2>&1
590 rm -f temp/staging.*.image > /dev/null 2>&1
591 if [[ -f $BOOT_IMAGE ]] ; then
592 get_url "$CHECKSUM_URL"
594 factorcode_md5=$(grep "$BOOT_IMAGE" checksums.txt | cut -f2 -d' ')
597 disk_md5=$($MD5SUM "$BOOT_IMAGE" | cut -f1 -d' ')
598 $ECHO "Factorcode md5: $factorcode_md5"
599 $ECHO "Disk md5: $disk_md5"
600 if [[ "$factorcode_md5" == "$disk_md5" ]] ; then
601 $ECHO "Your disk boot image matches the one on downloads.factorcode.org."
603 rm -f "$BOOT_IMAGE" > /dev/null 2>&1
612 $ECHO "Downloading boot image $BOOT_IMAGE."
613 get_url "${BOOT_IMAGE_URL}"
617 if [[ -z $DOWNLOADER_NAME ]] ; then
620 download_with_downloader "$1"
625 check_installed_programs
630 $ECHO "Copying $FACTOR_IMAGE to $FACTOR_IMAGE_FRESH..."
631 cp "$FACTOR_IMAGE" "$FACTOR_IMAGE_FRESH"
634 check_launch_factor() {
635 "./$FACTOR_BINARY" -e=
636 check_ret "Could not launch ./$FACTOR_BINARY"
639 is_boot_image_outdated() {
640 "./$FACTOR_BINARY" "-e=USE: system \"\" to-refresh 2drop length 0 > 1 0 ? exit"
646 if [[ -f $BOOT_IMAGE ]] ; then
647 get_url "$CHECKSUM_URL"
649 factorcode_md5=$(grep "$BOOT_IMAGE" checksums.txt | cut -f2 -d' ')
652 disk_md5=$($MD5SUM "$BOOT_IMAGE" | cut -f1 -d' ')
653 $ECHO "Boot image @factorcode.org md5: $factorcode_md5"
654 $ECHO "Boot image @local disk md5: $disk_md5"
655 if [[ "$factorcode_md5" == "$disk_md5" ]] ; then
656 $ECHO "Your disk boot image matches the one on downloads.factorcode.org."
658 $ECHO "Your disk boot image and the one on downloads.factorcode.org mismatch"
663 info_check_factor_refresh_all_locally() {
667 if is_boot_image_outdated; then
668 $ECHO "Your Factor image is consistent with the local source code."
670 $ECHO "Your Factor image is not consistent with the local source code."
675 ./$FACTOR_BINARY -i="$BOOT_IMAGE"
676 check_ret "./$FACTOR_BINARY bootstrap failed"
698 download_and_bootstrap() {
703 net_bootstrap_no_pull() {
706 download_and_bootstrap
710 ./$FACTOR_BINARY -e="USING: vocabs.loader vocabs.refresh system memory ; refresh-all save 0 exit"
715 ./$FACTOR_BINARY -run="bootstrap.image" "$MAKE_IMAGE_TARGET"
720 sudo apt install --yes libc6-dev libpango1.0-dev libx11-dev xorg-dev libgtk2.0-dev gtk2-engines-pixbuf libgtkglext1-dev wget git git-doc rlwrap clang make screen tmux libssl-dev
724 install_deps_pacman() {
725 sudo pacman --noconfirm -Syu gcc clang make rlwrap git wget pango glibc gtk2 gtk3 gtkglext gtk-engines gdk-pixbuf2 libx11 screen tmux
730 sudo dnf --assumeyes install gcc gcc-c++ glibc-devel binutils libX11-devel pango-devel gtk3-devel gdk-pixbuf2-devel gtkglext-devel tmux rlwrap wget
735 sudo pkg install --yes bash git gmake gcc rlwrap ripgrep curl gmake x11-toolkits/gtk30 x11-toolkits/gtkglext pango cairo vim
739 install_deps_macosx() {
740 if test_program_installed git; then
741 ensure_program_installed yes
742 $ECHO "git not found."
743 $ECHO "This script requires either git-core or port."
744 $ECHO "If it fails, install git-core or port and try again."
745 ensure_program_installed port
746 $ECHO "Installing git-core with port...this will take awhile."
747 yes | sudo port install git-core
752 $ECHO "usage: $0 command [optional-target]"
753 $ECHO " install - git clone, compile, bootstrap"
754 $ECHO " deps-apt - install required packages for Factor on Linux using apt"
755 $ECHO " deps-pacman - install required packages for Factor on Linux using pacman"
756 $ECHO " deps-dnf - install required packages for Factor on Linux using dnf"
757 $ECHO " deps-pkg - install required packages for Factor on FreeBSD using pkg"
758 $ECHO " deps-macosx - install git on MacOSX using port"
759 $ECHO " info-boot-image - print remote and disk boot image MD5"
760 $ECHO " info-check-factor-refresh-all-locally - check if local sources would cause refresh-all to change the image"
761 $ECHO " self-bootstrap - make local boot image, bootstrap"
762 $ECHO " self-update - git pull, recompile, make local boot image, bootstrap"
763 $ECHO " quick-update - git pull, refresh-all, save"
764 $ECHO " update|latest - git pull, recompile, download a boot image, bootstrap"
765 $ECHO " compile - compile the binary"
766 $ECHO " recompile - recompile the binary"
767 $ECHO " bootstrap - bootstrap with existing boot image"
768 $ECHO " net-bootstrap - recompile, download a boot image, bootstrap"
769 $ECHO " make-target - find and print the os-arch-cpu string"
770 $ECHO " report|info - print the build variables"
771 $ECHO " full-report - print the build variables, check programs and libraries"
772 $ECHO " update-boot-image - get the boot image for the current branch"
774 $ECHO "If you are behind a firewall, invoke as:"
775 $ECHO "env GIT_PROTOCOL=http $0 <command>"
777 $ECHO "Example for overriding the default target:"
778 $ECHO " $0 update macosx-x86-32"
783 # -n is nonzero length, -z is zero length
784 if [[ -n "$2" ]] ; then
785 parse_build_info "$2"
788 if [ "$#" -gt 3 ]; then
790 $ECHO "error: too many arguments"
796 deps-apt) install_deps_apt ;;
797 deps-pacman) install_deps_pacman ;;
798 deps-macosx) install_deps_macosx ;;
799 deps-dnf) install_deps_dnf ;;
800 deps-pkg) install_deps_pkg ;;
801 info-boot-image) info_boot_image ;;
802 info-check-factor-refresh-all-locally) info_check_factor_refresh_all_locally ;;
803 update-boot-image) find_build_info; check_installed_programs; update_boot_image ;;
804 self-bootstrap) get_config_info; make_boot_image; bootstrap ;;
805 self-update) update; make_boot_image; bootstrap ;;
806 quick-update) update; refresh_image ;;
807 update|latest) update; download_and_bootstrap ;;
808 compile) find_build_info; make_factor ;;
809 recompile) find_build_info; make_clean; make_factor ;;
810 bootstrap) get_config_info; bootstrap ;;
811 net-bootstrap) net_bootstrap_no_pull ;;
812 make-target) FIND_MAKE_TARGET=true; ECHO=false; find_build_info; exit_script 0;;
813 report|info) find_build_info ;;
814 full-report) find_build_info; check_installed_programs; check_libraries ;;
815 update-script) update_script ;;