#! /bin/bash # # Necessary preparations/configurations for the reproducible project. # # Copyright (C) 2018-2019 Mohammad Akhlaghi # # This script is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This script is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # A copy of the GNU General Public License is available at # . # Script settings # --------------- # Stop the script if there are any errors. set -e # Default option values jobs=0 build_dir= input_dir= host_cc=0 software_dir= existing_conf=0 minmapsize=10000000000 scriptname="./configure" # Output of --help # ---------------- print_help() { # Print the output. cat < "$1" then echo "#" >> "$1" echo "# This file was created during configuration" >> "$1" echo "# ('./configure'). Therefore, it is not under version" >> "$1" echo "# control and any manual changes to it will be" >> "$1" echo "# over-written if the project re-configured." >> "$1" echo "#" >> "$1" else echo; echo "Can't write to $1"; echo; exit 1 fi } # Get absolute address # -------------------- # # Since the build directory will go into a symbolic link, we want it to be # an absolute address. With this function we can make sure of that. function absolute_dir() { if stat "$1" 1> /dev/null; then echo "$(cd "$(dirname "$1")" && pwd )/$(basename "$1")" else exit 1; fi } # Inform the user # --------------- # # Print some basic information so the user gets a feeling of what is going # on and is prepared on what will happen next. cat < /dev/null 2>/dev/null; then name=$(which wget) # By default Wget keeps the remote file's timestamp, so we'll have # to disable it manually. downloader="$name --no-use-server-timestamps -O"; elif type curl > /dev/null 2>/dev/null; then name=$(which curl) # - cURL doesn't keep the remote file's timestamp by default. # - With the `-L' option, we tell cURL to follow redirects. downloader="$name -L -o" else cat < /dev/null; then bdir=$(absolute_dir $build_dir) echo " -- Build directory: '$bdir'" rm -rf $build_dir/$junkname else echo " -- Can't write in '$build_dir'" fi else if mkdir $build_dir 2> /dev/null; then bdir=$(absolute_dir $build_dir) echo " -- Build directory set to (the newly created): '$bdir'" else echo " -- Can't create '$build_dir'" fi fi # Reset `build_dir' to blank, so it continues asking when the # previous value wasn't usable. build_dir= done fi # Input directory # --------------- if [ x"$input_dir" = x ]; then indir=$optionaldir else indir=$input_dir fi wfpc2name=$(awk '!/^#/ && $1=="WFPC2IMAGE" {print $3}' $adir/INPUTS.mk) wfpc2md5=$(awk '!/^#/ && $1=="WFPC2MD5" {print $3}' $adir/INPUTS.mk) wfpc2size=$(awk '!/^#/ && $1=="WFPC2SIZE" {print $3}' $adir/INPUTS.mk) wfpc2url=$(awk '!/^#/ && $1=="WFPC2URL" {print $3}' $adir/INPUTS.mk) if [ $rewritepconfig = yes ] && [ x"$input_dir" = x ]; then cat <> $pconf else # Read the values from existing configuration file. inbdir=$(awk '$1=="BDIR" {print $3}' $pconf) # Read the software directory. ddir=$(awk '$1=="DEPENDENCIES-DIR" {print $3}' $pconf) # The downloader command may contain multiple elements, so we'll just # change the (in memory) first and second tokens to empty space and # write the full line (the original file is unchanged). downloader=$(awk '$1=="DOWNLOADER" {$1=""; $2=""; print $0}' $pconf) # Make sure all necessary variables have a value err=0 verr=0 novalue="" if [ x"$inbdir" = x ]; then novalue="BDIR, "; fi if [ x"$downloader" = x ]; then novalue="$novalue"DOWNLOADER; fi if [ x"$novalue" != x ]; then verr=1; err=1; fi # Make sure `bdir' is an absolute path and it exists. berr=0 ierr=0 bdir=$(absolute_dir $inbdir) if ! [ -d $bdir ]; then if ! mkdir $bdir; then berr=1; err=1; fi; fi if [ $err = 1 ]; then cat <> $glconf echo " minmapsize $minmapsize" >> $glconf echo >> $glconf echo "# Version of Gnuastro that must be used." >> $glconf echo " onlyversion $gversion" >> $glconf else ingversion=$(awk '$1=="onlyversion" {print $NF}' $glconf) if [ x$ingversion != x$gversion ]; then cat <" > $cprog #echo "int main(void) {return 0;}" >> $cprog #if [ x$CC = x ]; then CC=gcc; fi; #if $CC $cprog -o$oprog -static &> /dev/null; then # export static_build="yes" #else # export static_build="no" #fi #rm -f $oprog $cprog #if [ $printnotice = yes ] && [ $static_build = "no" ]; then # cat <" > $cprog echo "int main(void) {return 0;}" >> $cprog if [ x$CC = x ]; then CC=gcc; fi; if $CC $cprog -o$oprog -Wl,-rpath-link &> /dev/null; then export rpath_command="-Wl,-rpath-link=$instdir/lib" else export rpath_command="" fi rm -f $oprog $cprog # See if we need the dynamic-linker (-ldl) # ---------------------------------------- # # Some programs (like Wget) need dynamic loading (using `libdl'). On # GNU/Linux systems, we'll need the `-ldl' flag to link such programs. But # Mac OS doesn't need any explicit linking. So we'll check here to see if # it is present (thus necessary) or not. oprog=$sdir/ldl-test cprog=$sdir/ldl-test.c cat > $cprog < #include int main(void) { void *handle=dlopen ("/lib/CEDD_LIB.so.6", RTLD_LAZY); return 0; } EOF if gcc $cprog -o$oprog &> /dev/null; then needs_ldl=no; else needs_ldl=yes; fi rm -f $oprog $cprog # inform the user that the build process is starting # ------------------------------------------------- if [ $printnotice = yes ]; then tsec=10 cat < /dev/null 2>/dev/null; then host_cc=1 on_mac_os=yes else on_mac_os=no fi # Build `flock' # ------------- # # Flock (or file-lock) is a unique program that is necessary to serialize # the (generally parallel) processing of make when necessary. GNU/Linux # machines have it as part of their `util-linux' programs. But to be # consistent in non-GNU/Linux systems, we will be using our own build. # # The reason that `flock' is sepecial is that we need it to serialize the # download process of the software tarballs. flockversion=$(awk '/flock-version/{print $3}' $depverfile) flocktar=flock-$flockversion.tar.gz flockurl=http://github.com/discoteq/flock/releases/download/v$flockversion/ # Prepare/download the tarball. if ! [ -f $tardir/$flocktar ]; then if [ -f $ddir/$flocktar ]; then cp $ddir/$flocktar $tardir/$flocktar else if ! $downloader $tardir/$flocktar $flockurl/$flocktar; then rm -f $tardir/$flocktar; echo echo "DOWNLOAD ERROR: Couldn't download the 'flock' tarball:" echo " $flockurl" echo echo "You can manually place it in '$ddir' to avoid downloading." exit 1 fi fi fi # If the tarball is newer than the (possibly existing) program (the version # has changed), then delete the program. if [ -f .local/bin/flock ]; then if [ $tardir/$flocktar -nt $ibidir/flock ]; then rm $ibidir/flock fi fi # Build `flock' if necessary. if ! [ -f $ibidir/flock ]; then cd $tmpblddir tar xf $tardir/$flocktar cd flock-$flockversion ./configure --prefix=$instdir make make install cd $topdir rm -rf $tmpblddir/flock-$flockversion echo "Discoteq flock $flockversion" > $ibidir/flock fi # See if GCC can be built # ----------------------- # # On some GNU/Linux distros, the C compiler is broken into `multilib' (for # 32-bit and 64-bit support, with their own headers). On these systems, # `/usr/include/sys/cdefs.h' and `/usr/lib/libc.a' are not available by # default. So GCC will crash with different ugly errors! The only solution # is that user manually installs the `multilib' part as root, before # running the configure script. # # Note that `sys/cdefs.h' may be available in other directories (for # example `/usr/include/x86_64-linux-gnu/') that are automatically included # in an installed GCC. HOWEVER during the build of GCC, all those other # directories are ignored. So even if they exist, they are useless. warningsleep=0 if [ $host_cc = 0 ]; then if ! [ -f /usr/include/sys/cdefs.h ]; then host_cc=1 warningsleep=1 cat < /dev/null; then if [ $jobs = 0 ]; then numthreads=$(nproc --all); else numthreads=$jobs fi else numthreads=1; fi # Build basic software # -------------------- # # When building these software we don't have our own un-packing software, # Bash, Make, or AWK. In this step, we'll install such low-level basic # tools, but we have to be very portable (and use minimal features in all). make -f reproduce/software/make/basic.mk \ rpath_command=$rpath_command \ static_build=$static_build \ needs_ldl=$needs_ldl \ on_mac_os=$on_mac_os \ numthreads=$numthreads \ host_cc=$host_cc \ -j$numthreads # All other software # ------------------ # # We will be making all the dependencies before running the top-level # Makefile. To make the job easier, we'll do it in a Makefile, not a # script. Bash and Make were the tools we need to run Makefiles, so we had # to build them in this script. But after this, we can rely on Makefiles. if [ $jobs = 0 ]; then numthreads=$($instdir/bin/nproc --all) else numthreads=$jobs fi ./.local/bin/make -f reproduce/software/make/high-level.mk \ rpath_command=$rpath_command \ static_build=$static_build \ on_mac_os=$on_mac_os \ numthreads=$numthreads \ -j$numthreads # Make sure TeX Live installed successfully # ----------------------------------------- # # TeX Live is managed over the internet, so if there isn't any, or it # suddenly gets cut, it can't be built. However, when TeX Live isn't # installed, the project can do all its processing independent of it. It # will just stop at the stage when all the processing is complete and it is # only necessary to build the PDF. So we don't want to stop the project's # configuration and building if its not present. texlive_result=$(cat $itidir/texlive-ready-tlmgr) if [ x"$texlive_result" = x"NOT!" ]; then cat <0 { \ c++; \ if(c==1) \ { \ if('$num'==1) printf("%s", $0); \ else printf("%s", $0); \ } \ else if(c=='$num') printf(" and %s\n", $0); \ else printf(", %s", $0) \ }' fi } # Report the different software in separate contexts (separating Python and # TeX packages from the C/C++ programs and libraries). proglibs=$(prepare_name_version $verdir/proglib/*) pymodules=$(prepare_name_version $verdir/python/*) texpkg=$(prepare_name_version $verdir/tex/texlive) # Write them as one paragraph for LaTeX. pkgver=$mtexdir/dependencies.tex .local/bin/echo "This research was done with the following free" > $pkgver .local/bin/echo "software programs and libraries: $proglibs." >> $pkgver if [ x"$pymodules" != x ]; then .local/bin/echo "Within Python, the following modules" >> $pkgver echo "were used: $pymodules." >> $pkgver fi .local/bin/echo "The \LaTeX{} source of the paper was compiled" >> $pkgver .local/bin/echo "to make the PDF using the following packages:" >> $pkgver .local/bin/echo "$texpkg. We are very grateful to all their" >> $pkgver .local/bin/echo "creators for freely providing this necessary" >> $pkgver .local/bin/echo "infrastructure. This research (and many " >> $pkgver .local/bin/echo "others) would not be possible without them." >> $pkgver # Prepare the BibTeX entries for the used software (if there are any). hasentry=0 bibfiles="$ictdir/*" for f in $bibfiles; do if [ -f $f ]; then hasentry=1; break; fi; done; # Make sure we start with an empty output file. pkgbib=$mtexdir/dependencies-bib.tex echo "" > $pkgbib # Fill it in with all the BibTeX entries in this directory. We'll just # avoid writing any comments (usually copyright notices) and also put an # empty line after each file's contents to make the output more readable. if [ $hasentry = 1 ]; then for f in $bibfiles; do awk '!/^%/{print} END{print ""}' $f >> $pkgbib done fi # Clean the temporary build directory # --------------------------------- # # By the time the script reaches here the temporary software build # directory should be empty, so just delete it. rm -rf $tmpblddir # Final step: available Makefile # ------------------------------ # # We only want `make' to work after the configuration is complete. So we # will only put in the top-level Makefile after all the steps above are # done. .local/bin/ln -s $topdir/reproduce/analysis/make/top.mk Makefile # Final notice # ------------ # # The configuration is now complete, we can inform the user on the next # step(s) to take. if [ x$reproducible_paper_group_name = x ]; then buildcommand=".local/bin/make -j8" else buildcommand="./for-group $reproducible_paper_group_name make -j8" fi cat <