# Build the VERY BASIC project software before higher-level ones. Assuming
# minimal/generic Make and Shell.
#
# ------------------------------------------------------------------------
#                      !!!!! IMPORTANT NOTES !!!!!
#
# This Makefile will be run by the initial './project configure' script. It
# is not included into the project afterwards.
#
# This Makefile builds low-level and basic tools that are necessary in any
# project like like GNU Tar, GNU Bash, GNU Make, GCC and etc. But before
# control reaches here, the 'configure.sh' script has already built the
# extremely low-level tools: Lzip (a compressing program), GNU Make (to be
# able to run this Makefile with a fixed version), Dash (a minimalist
# POSIX-compatible shell) and Flock (to allow locking files, and
# serializing when necessary: downloading during the software building
# phase). Thanks to GNU Make and Dash, we can assume a fixed structure in
# this Makefile. However, the 'PATH's in this Makefile still include the
# host's paths because we will be using the hosts tools (gradually
# decreasing) to build our own tools.
#
# ------------------------------------------------------------------------
#
# Copyright (C) 2018-2025 Mohammad Akhlaghi <mohammad@akhlaghi.org>
# Copyright (C) 2019-2025 Raul Infante-Sainz <infantesainz@gmail.com>
# Copyright (C) 2022-2025 Pedram Ashofteh Ardakani <pedramardakani@pm.me>
#
# This Makefile 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 Makefile 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.
#
# You should have received a copy of the GNU General Public License
# along with this Makefile.  If not, see <http://www.gnu.org/licenses/>.

# Top level environment
include reproduce/software/config/LOCAL.conf
include reproduce/software/make/build-rules.mk
include reproduce/software/config/versions.conf
include reproduce/software/config/checksums.conf

# The optional URLs of software. Note that these may need the software
# version, so it is important that they be loaded after 'versions.conf'.
include reproduce/software/config/urls.conf

# Basic directories
lockdir  = $(BDIR)/software/locks
tdir     = $(BDIR)/software/tarballs
ddir     = $(BDIR)/software/build-tmp
idir     = $(BDIR)/software/installed
ibdir    = $(BDIR)/software/installed/bin
ildir    = $(BDIR)/software/installed/lib
iidir    = $(BDIR)/software/installed/include
shsrcdir = "$(shell pwd)"/reproduce/software/shell
ibidir   = $(BDIR)/software/installed/version-info/proglib

# Ultimate Makefile target. GNU Nano (a simple and very light-weight text
# editor) is installed by default, it is recommended to have it in the
# 'basic.mk', so Maneaged projects can be edited on any system (even when
# there is no command-line text editor available).
targets-proglib = low-level-links \
                  gcc-$(gcc-version) \
                  nano-$(nano-version)
all: $(foreach p, $(targets-proglib), $(ibidir)/$(p))

# Define the shell environment
# ----------------------------
#
# We build GNU Bash here in 'basic.mk'. So here we must must assume DASH
# shell that was built before calling this Makefile:
# http://gondor.apana.org.au/~herbert/dash. DASH is a minimalist POSIX
# shell, so it doesn't have startup options like '--noprofile --norc'. But
# from its manual, to load startup files, Dash actually requires that it be
# called with a '-' before it (for example '-dash'), so it shouldn't be
# loading any startup files if it was interpretted properly.
#
# As we build more programs, we want to use this project's built programs
# and libraries, not the host's, so in all PATH-related environments, our
# own build-directory comes first.
.ONESHELL:
.SHELLFLAGS := -e -c
export CCACHE_DISABLE := 1
export SHELL := $(ibdir)/dash
export PATH := $(ibdir):$(PATH)
export PKG_CONFIG_PATH := $(ildir)/pkgconfig
export PKG_CONFIG_LIBDIR := $(ildir)/pkgconfig
export LDFLAGS := $(rpath_command) -L$(ildir) $(LDFLAGS)

# Disable built-in rules (which are not needed here!)
.SUFFIXES:

# See description of '-Wno-nullability-completeness' in
# 'reproduce/software/shell/configure.sh'.
ifeq ($(on_mac_os),yes)
  noccwarnings=-Wno-nullability-completeness
endif
export CPPFLAGS := -I$(idir)/include $(CPPFLAGS) $(noccwarnings)

# This is the "basic" tools where we are relying on the host operating
# system, but are slowly populating our basic software envirnoment. To run
# (system or template) programs, 'LD_LIBRARY_PATH' is necessary, so here,
# we'll first tell the programs to look into any possible pre-defined
# 'LD_LIBRARY_PATH', then we'll add our own newly installed libraries.  We
# will also make sure that there is no "current directory" in it (by
# removing a starting or trailing ':' and any occurance of '::'.
#
# But first: in case LD_LIBRARY_PATH is empty, give it the default value of
# $(sys_library_sh_path) (which was the location of the libraries needed by
# the host's shell). This is because after we add the Maneage's library
# path, on some systems, no other libraries will be checked except those
# that are in 'LD_LIBRARY_PATH'.
ifeq ($(strip $(LD_LIBRARY_PATH)),)
export LD_LIBRARY_PATH=$(sys_library_sh_path)
endif
export LD_LIBRARY_PATH := $(shell echo $(LD_LIBRARY_PATH):$(ildir) \
                                  | sed -e's/::/:/g' -e's/^://' -e's/:$$//')

# RPATH is automatically written in macOS, so 'DYLD_LIBRARY_PATH' is
# ultimately redundant. But on some systems, even having a single value
# causes crashs (see bug #56682). So we'll just give it no value at all.
export DYLD_LIBRARY_PATH :=

# Servers to use as backup. Maneage already has some fixed servers that can
# be used to download software tarballs. They are in a configuation
# file. But we give precedence to the "user" backup servers.
#
# One important "user" server (which the user doesn't actually give, but is
# found at configuration time in 'configure.sh') is Zenodo (see the
# description in 'configure.sh' for more on why this depends on
# configuration time).
#
# Afer putting everything together, we use the first server as the
# reference for all software if their '-url' variable isn't defined (in
# 'reproduce/software/config/urls.conf').
downloadwrapper = ./reproduce/analysis/bash/download-multi-try.sh
maneage_backup_urls := $(shell awk '!/^#/{printf "%s ", $$1}' \
                               reproduce/software/config/servers-backup.conf)
backupservers_all = $(user_backup_urls) $(maneage_backup_urls)
topbackupserver = $(word 1, $(backupservers_all))
backupservers = $(filter-out $(topbackupserver),$(backupservers_all))










# Low-level (not built) programs
# ------------------------------
#
# For the time being, some components of the project aren't being built on
# some systems (primarily on proprietary operating systems). So we are
# simply making a symbolic link to the system's programs/libraries in the
# build directory.
#
# The logical position of this rule is irrelevant in this Makefile (because
# programs being built here have full access to the system's PATH
# already). This is done for the high-level programs installed in
# 'high-level.mk', 'xorg.mk' or 'python.mk'. So this step is done after
# building our own GNU Grep (which is the highest-level program used in
# 'makelink') to have trustable elements.
#
# About ccache: ccache acts like a wrapper over the C compiler and is made
# to avoid/speed-up compiling of identical files in a system (it is
# commonly used on large servers). It actually makes 'gcc' or 'g++' a
# symbolic link to itself so it can control them internally. So, for our
# purpose here, it is very annoying and can cause many complications. We
# thus remove any part of PATH of that has 'ccache' in it before making
# symbolic links to the programs we are not building ourselves.
#
# The double quotations after the starting 'export PATH' are necessary in
# case the user's PATH has space-characters in it.
#
# We use 'realpath' here (part of GNU Coreutils which is already installed
# by the time we use 'makelink') to avoid linking to a link (on the
# host). 'realpath' will follow a link (and possibly other links in the
# middle) to an actual file and return its address. When the location isn't
# a link, it will just return it.
syspath := $(PATH)
makelink = origpath="$$PATH"; \
	export PATH="$$(echo $(syspath) \
	                     | tr : '\n' \
	                     | grep -v ccache \
	                     | tr '\n' :)"; \
	if type $(1) > /dev/null 2> /dev/null; then \
	  if [ x$(3) = x ]; then \
	    ln -sf "$$(realpath $$(command -v $(1)))" $(ibdir)/$(1); \
	  else \
	    ln -sf "$$(realpath $$(command -v $(1)))" $(ibdir)/$(3); \
	  fi; \
	else \
	  if [ "x$(strip $(2))" = xmandatory ]; then \
	    echo "'$(1)' is necessary for higher-level tools."; \
	    echo "Please install it for the configuration to continue."; \
	    exit 1; \
	  fi; \
	fi; \
	export PATH="$$origpath"

$(ibdir) $(ildir):; mkdir $@
$(ibidir)/low-level-links: $(ibidir)/grep-$(grep-version) \
                           | $(ibdir) $(ildir)

#	Hardware specific
	$(call makelink,lp)    # For printing, necessary for R.
	$(call makelink,lpr)   # For printing, necessary for R.

#	Mac OS specific
	$(call makelink,mig)
	$(call makelink,xcrun)
	$(call makelink,sysctl)
	$(call makelink,sw_vers)
	$(call makelink,codesign)
	$(call makelink,dsymutil)
	$(call makelink,install_name_tool)

#	On Mac OS, libtool is different compared to GNU Libtool. The
#	libtool we'll build in the high-level dependencies has the
#	executable name 'glibtool'.
	$(call makelink,libtool)

#	Necessary libraries:
#	    Libdl (for dynamic loading libraries at runtime)
#	    POSIX Threads library for multi-threaded programs.
	for l in dl pthread; do
	  if [ -f /usr/lib/lib$$l.a ]; then
	    for f in /usr/lib/lib$$l.*; do
	      ln -sf $$(realpath $$f) \
	         $$(echo $$f | sed -e's|/usr/lib|$(ildir)|')
	    done
	  fi
	done

#	Useful tools: 'ldd' (list libraries linked by binary on GNU
#	systems)
	$(call makelink,ldd)

#	We want this to be empty (so it doesn't interefere with the other
#	files in 'ibidir'.
	touch $@










# Level 1 (MOST BASIC): Compression programs
# ------------------------------------------
#
# The first set of programs to be built are those that we need to unpack
# the source code tarballs of each program. We have already installed Lzip
# before calling 'basic.mk', so it is present and working. Hence we first
# build the Lzipped tarball of Gzip, then use our own Gzip to unpack the
# tarballs of the other compression programs. Once all the compression
# programs/libraries are complete, we build our own GNU Tar and continue
# with other software.
$(lockdir): | $(BDIR); mkdir $@
$(ibidir)/gzip-$(gzip-version): | $(ibdir) $(ildir) $(lockdir)
	tarball=gzip-$(gzip-version).tar.lz
	$(call import-source, $(gzip-url), $(gzip-checksum))
	$(call gbuild, gzip-$(gzip-version), static, , V=1)
	echo "GNU Gzip $(gzip-version)" > $@

# 2022-07-14 B Roukema
#
# xz-5.2.5 fails on (at least) CentOS 7 (Redhat) systems while trying
# to compile 'cmake' in Maneage - this is Maneage bug 62700 [1].
#
# The fix appears to be just a few lines, although it's not clear
# how robust or long-term it is. Since we don't yet have 'patch' in
# 'basic.mk', this file has to be copied into place rather than patched.

# xz-5.2.5_src_liblzma_liblzma.map is a patched
# version of xz-5.2.5/src/liblzma/liblzma.map based on discussion at
# [1] + [2] + the patch file [3].
#
# [1] https://savannah.nongnu.org/bugs/index.php?62700
# [2] https://github.com/easybuilders/easybuild-easyconfigs/issues/14991
# [3] https://raw.githubusercontent.com/easybuilders/easybuild-easyconfigs/bcebb3320ffb63f9804ca8d4d64d1822ec7c9792/easybuild/easyconfigs/x/XZ/XZ-5.2.5_compat-libs.patch
$(ibidir)/xz-$(xz-version): $(ibidir)/gzip-$(gzip-version)

#	Prepare the tarball.
	tarball=xz-$(xz-version).tar.lz
	$(call import-source, $(xz-url), $(xz-checksum))

#	Until the bug mentioned above is fixed, we'll can't use the generic
#	rule.
#	$(call gbuild, xz-$(xz-version), static)

#	Configure and build with patched file.
	srcdir=$$(pwd)
	unpackdir=xz-$(xz-version)
	patchedfile=xz-5.2.5_src_liblzma_liblzma.map
	cd $(ddir)
	rm -rf $$unpackdir
	tar -x -f $(tdir)/$$tarball
	cd $$unpackdir
	cp -pv $$srcdir/reproduce/software/patches/$$patchedfile \
	       src/liblzma/liblzma.map # copy the fixed file into place
	./configure --prefix=$(idir)
	make install
	cd ..
	rm -rf $$unpackdir
	echo "XZ Utils $(xz-version)" > $@

$(ibidir)/bzip2-$(bzip2-version): $(ibidir)/gzip-$(gzip-version)

#	Download the tarball.
	tarball=bzip2-$(bzip2-version).tar.lz
	$(call import-source, $(bzip2-url), $(bzip2-checksum))

#	Bzip2 doesn't have a './configure' script, and its Makefile doesn't
#	build a shared library. So we can't use the 'gbuild' function here
#	and we need to take some extra steps (inspired from the GNU/Linux
#	from Scratch (LFS) guide for Bzip2):
#
#	    1) The 'sed' call is for relative installed symbolic links.
#	    2) The special Makefile-libbz2_so builds shared libraries.
#
#	NOTE: the major version number appears in the final symbolic link.
	tdir=bzip2-$(bzip2-version)
	if [ $(static_build) = yes ]; then
	  makecommand="make LDFLAGS=-static"
	  makeshared="echo no-shared"
	else
	  makecommand="make"
	  if [ x$(on_mac_os) = xyes ]; then
	    makeshared="echo no-shared"
	  else
	    makeshared="make -f Makefile-libbz2_so"
	  fi
	fi
	cd $(ddir)
	rm -rf $$tdir
	tar -xf $(tdir)/$$tarball
	cd $$tdir
	$(shsrcdir)/prep-source.sh $(ibdir)
	sed -e 's@\(ln -s -f \)$$(PREFIX)/bin/@\1@' Makefile \
	    > Makefile.sed
	mv Makefile.sed Makefile
	$$makeshared CC=cc
	cp -a libbz2* $(ildir)/
	make clean
	$$makecommand CC=cc
	make install PREFIX=$(idir)
	cd ..
	rm -rf $$tdir
	cd $(ildir)
	ln -fs libbz2.so.$(bzip2-version) libbz2.so
	echo "Bzip2 $(bzip2-version)" > $@

# Some programs (like Wget and CMake) that use zlib need it to be dynamic
# so they use our custom build. So we won't force a static-only build.
#
# Note for a static-only build: Zlib's './configure' doesn't use Autoconf's
# configure script, it just accepts a direct '--static' option.
$(ibidir)/zlib-$(zlib-version): $(ibidir)/gzip-$(gzip-version)
	tarball=zlib-$(zlib-version).tar.lz
	$(call import-source, $(zlib-url), $(zlib-checksum))
	$(call gbuild, zlib-$(zlib-version))
	echo "Zlib $(zlib-version)" > $@

# GNU Tar: When built statically, tar gives a segmentation fault on
# unpacking Bash. So we'll build it dynamically. Note that technically, zip
# and unzip aren't dependencies of Tar, but for a clean build, we'll set
# Tar to be the last compression-related software (the first-set of
# software to be built).
$(ibidir)/tar-$(tar-version): \
              $(ibidir)/xz-$(xz-version) \
              $(ibidir)/gzip-$(gzip-version) \
              $(ibidir)/zlib-$(zlib-version) \
              $(ibidir)/bzip2-$(bzip2-version)

#	Since all later programs depend on Tar, the configuration will hit
#	a bottleneck here: only making Tar. So its more efficient to built
#	it on multiple threads (even when the user's Make doesn't pass down
#	the number of threads).
	tarball=tar-$(tar-version).tar.lz
	$(call import-source, $(tar-url), $(tar-checksum))
	$(call gbuild, tar-$(tar-version), , , -j$(numthreads) V=1)
	echo "GNU Tar $(tar-version)" > $@










# Level 2 (necessary for linking)
#
# Patchelf is necessary for some software on GNU/Linux systems, its job is
# to manually insert RPATH into the dynamically-linked executable. Since
# all the other software depend on Pathelf, to avoid manually repeating as
# a prerequisite (and forgetting in others causing bugs), we'll put it as a
# dependancy of 'tar'.
$(ibidir)/patchelf-$(patchelf-version): $(ibidir)/tar-$(tar-version)
	tarball=patchelf-$(patchelf-version).tar.lz
	$(call import-source, $(patchelf-url), $(patchelf-checksum))
	if [ x$(on_mac_os) = xyes ]; then
	  echo "" > $@
	else
	  $(call gbuild, patchelf-$(patchelf-version))
	  echo "PatchELF $(patchelf-version)" > $@
	fi










# Level 3 (THIRD MOST BASIC): Bash
# --------------------------------
#
# GNU Make and GNU Bash are the second layer that we'll need to build the
# basic dependencies.
#
# Unfortunately Make needs dynamic linking in two instances: when loading
# objects (dynamically linked libraries), or when using the 'getpwnam'
# function (for tilde expansion). The first can be disabled with
# '--disable-load', but unfortunately I don't know any way to fix the
# second. So, we'll have to build it dynamically for now.
$(ibidir)/ncurses-$(ncurses-version): $(ibidir)/patchelf-$(patchelf-version)
	tarball=ncurses-$(ncurses-version).tar.lz
	$(call import-source, $(ncurses-url), $(ncurses-checksum))

#	Delete the library that will be installed (so we can make sure the
#	build process completed afterwards and reset the links).
	rm -f $(ildir)/libncursesw*

#	Delete the (possibly existing) low-level programs that depend on
#	'readline', and thus 'ncurses'. Since these programs are actually
#	used during the building of 'ncurses', we need to delete them so
#	the build process doesn't use the project's Bash and AWK, but the
#	host's.
	rm -f $(ibdir)/bash* $(ibdir)/awk* $(ibdir)/gawk*

#	Standard build process.
	$(call gbuild, ncurses-$(ncurses-version), static, \
	               --with-shared --enable-rpath --without-normal \
	               --without-debug --with-cxx-binding \
	               --with-cxx-shared --enable-widec --enable-pc-files \
	               --with-pkg-config=$(ildir)/pkgconfig, -j$(numthreads))

#	Unfortunately there are many problems with 'ncurses' using "normal"
#	(or 8-bit) characters. The standard way that will work is to build
#	it with wide character mode as you see above in the configuration
#	(or the 'w' prefix you see below). Also, most programs (and in
#	particular Bash and AWK), first look for other (mostly obsolete)
#	libraries like tinfo, which define the same symbols. The links
#	below address both situations: we need to fool higher-level
#	packages to find this library even if they aren't explicitly
#	mentioning its name correctly (as a value to '-l' at link time in
#	their configure scripts).
#
#	This part is taken from the Arch GNU/Linux build script[1], then
#	extended to Mac thanks to Homebrew's script [2].
#
#	   [1] https://git.archlinux.org/svntogit/packages.git/tree/trunk/PKGBUILD?h=packages/ncurses
#	   [2] https://github.com/Homebrew/homebrew-core/blob/master/Formula/ncurses.rb
#
#	Since we can't have comments, in the connected script, here is a
#	summary:
#
#	  1. We find the actual suffix of the library, from the file that
#	     is not a symbolic link (starting with '-' in the output of 'ls
#	     -l').
#
#	  2. We make symbolic links to all the "ncurses", "ncurses++",
#	     "form", "panel" and "menu" libraries to point to their "wide"
#	     (character) library.
#
#	  3. We make symbolic links to the "tic" and "tinfo" libraries to
#	     point to the same 'libncursesw' library.
#
#	  4. Some programs link with "curses" (not "ncurses", notice the
#	     starting "n"), so we'll also make links for these to point to
#	     the 'libncursesw' library.
#
#	  5. A link is made to also be able to include files from the
#            'ncurses' headers.
	if [ x$(on_mac_os) = xyes ]; then so="dylib"; else so="so"; fi
	if [ -f $(ildir)/libncursesw.$$so ]; then

	  unalias ls || true # avoid decorated 'ls' commands with extra characters
	  sov=$$(ls -l $(ildir)/libncursesw* \
	               | awk '/^-/{print $$NF}' \
	               | sed -e "s;$(ildir)/libncursesw\.;;")

	  cd "$(ildir)"
	  for lib in ncurses ncurses++ form panel menu; do
	    ln -fs lib$$lib"w".$$sov     lib$$lib.$$so
	    ln -fs $(ildir)/pkgconfig/"$$lib"w.pc pkgconfig/$$lib.pc
	  done
	  for lib in tic tinfo; do
	    ln -fs libncursesw.$$sov     lib$$lib.$$so
	    ln -fs libncursesw.$$sov     lib$$lib.$$sov
	    ln -fs $(ildir)/pkgconfig/ncursesw.pc pkgconfig/$$lib.pc
	  done
	  ln -fs libncursesw.$$sov libcurses.$$so
	  ln -fs libncursesw.$$sov libcursesw.$$sov
	  ln -fs $(ildir)/pkgconfig/ncursesw.pc pkgconfig/curses.pc
	  ln -fs $(ildir)/pkgconfig/ncursesw.pc pkgconfig/cursesw.pc

	  ln -fs $(idir)/include/ncursesw $(idir)/include/ncurses
	  echo "GNU NCURSES $(ncurses-version)" > $@
	else
	  exit 1
	fi

$(ibidir)/readline-$(readline-version): \
                   $(ibidir)/ncurses-$(ncurses-version)
	tarball=readline-$(readline-version).tar.lz
	$(call import-source, $(readline-url), $(readline-checksum))
	$(call gbuild, readline-$(readline-version), static, \
	               --with-curses --disable-install-examples, \
	               SHLIB_LIBS="-lncursesw" -j$(numthreads))
	echo "GNU Readline $(readline-version)" > $@


# IMPORTANT: Even though we have enabled 'rpath', Bash doesn't write the
# absolute adddress of the libraries it depends on! Therefore, if we
# configure Bash with '--with-installed-readline' (so the installed version
# of Readline, that we build below as a prerequisite or AWK, is used) and
# you run 'ldd $(ibdir)/bash' on the resulting binary, it will say that it
# is linking with the system's 'readline'. But if you run that same command
# within a rule in this project, you'll see that it is indeed linking with
# our own built readline.
#
# Unfortunately Bash doesn't maintain a Git repository and minor fixes are
# released as patches. Therefore we'll need to make our own fully-working
# and updated tarball to build the proper version of Bash. You download and
# apply them to the original tarball and make a new one with the following
# series of commands (just replace 'NUMBER' with the total number of
# patches that you want to apply).
#
#   $ number=NUMBER
#   $ tar -xf bash-5.0.tar.gz
#   $ cd bash-5.0
#   $ for i in $(seq 1 $number); do \
#       pname=bash50-$(printf "%03d" $i); \
#       wget http://ftp.gnu.org/gnu/bash/bash-5.0-patches/$pname -O ../$pname;\
#       patch -p0 -i ../$pname; \
#     done
#   $ cd ..
#   $ mv bash-5.0 bash-5.0.$number
#   $ tar cf bash-5.0.$number.tar bash-5.0.$number
#   $ lzip --best bash-5.0.$number.tar
#   $ rm -rf bash50-* bash-5.0.$number bash-5.0.tar.gz
$(ibidir)/bash-$(bash-version): \
               $(ibidir)/gettext-$(gettext-version) \
               $(ibidir)/readline-$(readline-version)

#	Download the tarball.
	tarball=bash-$(bash-version).tar.lz
	$(call import-source, $(bash-url), $(bash-checksum))

#	Delete the (possibly) existing Bash executable in the project,
#	let it use the default shell of the host.
	rm -f $(ibdir)/bash

#	Bash has many '--enable' features which are already enabled by
#	default. As described in the manual, they are mainly useful when
#	you disable them all with '--enable-minimal-config' and enable a
#	subset using the '--enable' options.
	if [ "x$(static_build)" = xyes ]; then stopt="--enable-static-link"
	else                                   stopt=""
	fi;
	export CFLAGS="$$CFLAGS \
	               -DDEFAULT_PATH_VALUE='\"$(ibdir)\"' \
	               -DSTANDARD_UTILS_PATH='\"$(ibdir)\"'  \
	               -DSYS_BASHRC='\"$(BASH_ENV)\"' "
	$(call gbuild, bash-$(bash-version),, $$stopt \
	               --with-installed-readline=$(ildir) \
	               --with-curses=yes, \
	               -j$(numthreads))

#	Atleast on GNU/Linux systems, Bash doesn't include RPATH by
#	default. So, we have to manually include it, currently we are only
#	doing this on GNU/Linux systems (using the 'patchelf' program).
	if [ -f $(ibdir)/patchelf ]; then
	  $(ibdir)/patchelf --set-rpath $(ildir) $(ibdir)/bash;
	fi

#	To be generic, some systems use the 'sh' command to call the
#	shell. By convention, 'sh' is just a symbolic link to the preferred
#	shell executable. So we'll define '$(ibdir)/sh' as a symbolic link
#	to the Bash that we just built and installed.
#
#	Just to be sure that the installation step above went well, before
#	making the link, we'll see if the file actually exists there.
	ln -fs $(ibdir)/bash $(ibdir)/sh
	echo "GNU Bash $(bash-version)" > $@










# Level 4: Most other programs
# ----------------------------

# In Perl, The '-shared' flag will cause problems while building on macOS,
# so we'll only use this configuration option when we are GNU/Linux
# systems. However, since the whole option must be used (which includes '='
# and empty space), its easier to define the variable as a Make variable
# outside the recipe, not as a shell variable inside it.
ifeq ($(on_mac_os),yes)
perl-conflddlflags =
else
perl-conflddlflags = -Dlddlflags="-shared $$LDFLAGS"
endif
$(ibidir)/perl-$(perl-version): $(ibidir)/patchelf-$(patchelf-version)
	tarball=perl-$(perl-version).tar.lz
	$(call import-source, $(perl-url), $(perl-checksum))
	major_version=$$(echo $(perl-version) \
	                     | sed -e's/\./ /g' \
	                     | awk '{printf("%d", $$1)}')
	base_version=$$(echo $(perl-version) \
	                     | sed -e's/\./ /g' \
	                     | awk '{printf("%d.%d", $$1, $$2)}')
	cd $(ddir)
	rm -rf perl-$(perl-version)
	tar -xf $(tdir)/$$tarball
	cd perl-$(perl-version)
	$(shsrcdir)/prep-source.sh $(ibdir)
	./Configure -des \
	            -Dusethreads \
	            -Duseshrplib \
	            -Dprefix=$(idir) \
	            -Dvendorprefix=$(idir) \
	            -Dprivlib=$(idir)/share/perl$$major_version/core_perl \
	            -Darchlib=$(idir)/lib/perl$$major_version/$$base_version/core_perl \
	            -Dsitelib=$(idir)/share/perl$$major_version/site_perl \
	            -Dsitearch=$(idir)/lib/perl$$major_version/$$base_version/site_perl \
	            -Dvendorlib=$(idir)/share/perl$$major_version/vendor_perl \
	            -Dvendorarch=$(idir)/lib/perl$$major_version/$$base_version/vendor_perl \
	            -Dscriptdir=$(idir)/bin/core_perl \
	            -Dsitescript=$(idir)/bin/site_perl \
	            -Dvendorscript=$(idir)/bin/vendor_perl \
	            -Dinc_version_list=none \
	            -Dman1ext=1perl \
	            -Dman3ext=3perl \
	            -Dcccdlflags='-fPIC' \
	            $(perl-conflddlflags) \
	            -Dldflags="$$LDFLAGS"
	make -j$(numthreads) V=1
	make install
	cd ..
	rm -rf perl-$(perl-version)
	cd $$topdir
	echo "Perl $(perl-version)" > $@





# Coreutils
# ---------
#
# For some reason, Coreutils doesn't include 'rpath' in its installed
# executables (even though it says that by default its included and that
# even when calling '--enable-rpath=yes'). So we have to manually add
# 'rpath' to Coreutils' executables after the standard build is
# complete.
#
# One problem is that Coreutils installs many very basic executables which
# might be in used by other programs. So we must make sure that when
# Coreutils is being built, no other program is being built in
# parallel. The solution to the many executables it installs is to make a
# fake installation (with 'DESTDIR'), and get a list of the contents of the
# directory to find the names.
#
# The echo after the PatchELF loop is to avoid a crash if the last
# file that PatchELF encounters is not usable (and it returns with
# an error).
#
# Coreutils uses Perl to create man pages!
$(ibidir)/coreutils-$(coreutils-version): \
                    $(ibidir)/bash-$(bash-version) \
                    $(ibidir)/perl-$(perl-version) \
                    $(ibidir)/openssl-$(openssl-version)

#	Import the source tarball.
	tarball=coreutils-$(coreutils-version).tar.lz
	$(call import-source, $(coreutils-url), $(coreutils-checksum))

#	Unpack and enter the source.
	cd $(ddir)
	rm -rf coreutils-$(coreutils-version)
	tar -xf $(tdir)/$$tarball
	cd coreutils-$(coreutils-version)
	$(shsrcdir)/prep-source.sh $(ibdir)

#	Configure, build and install Coreutils.
	./configure --prefix=$(idir) SHELL=$(ibdir)/bash  \
	            LDFLAGS="$(LDFLAGS)" CPPFLAGS="$(CPPFLAGS)" \
	            --disable-silent-rules --with-openssl=yes
	make SHELL=$(ibdir)/bash -j$(numthreads)
	make SHELL=$(ibdir)/bash install

#	Fix RPATH if necessary.
	if [ -f $(ibdir)/patchelf ]; then
	  make SHELL=$(ibdir)/bash install DESTDIR=junkinst
	  unalias ls || true # avoid decorated 'ls' commands with extra characters
	  instprogs=$$(ls junkinst/$(ibdir))
	  for f in $$instprogs; do
	    $(ibdir)/patchelf --set-rpath $(ildir) $(ibdir)/$$f
	  done
	  echo "PatchELF applied to all programs."
	fi

#	Come back up to the unpacking directory, delete the source
#	directory and write the final target.
	cd ..
	rm -rf coreutils-$(coreutils-version)
	echo "GNU Coreutils $(coreutils-version)" > $@

# Podlators
#
# POD is short for "Plain Old Documentation", that is the format used in
# Perl's documentation. Podlators provies two executables pod2man and
# pod2text convert this into the roff format (used in man pages) or pod2 It
# is used by some software like OpenSSL to create their man pages.
$(ibidir)/podlators-$(podlators-version): $(ibidir)/perl-$(perl-version)
	tarball=podlators-$(podlators-version).tar.lz
	$(call import-source, $(podlators-url), $(podlators-checksum))
	cd $(ddir)
	rm -rf podlators-$(podlators-version)
	tar -xf $(tdir)/$$tarball
	cd podlators-$(podlators-version)
	$(shsrcdir)/prep-source.sh $(ibdir)
	perl Makefile.PL
	make
	make install
	ln -sf $(ibdir)/site_perl/pod2man $(ibdir)/pod2man
	ln -sf $(ibdir)/site_perl/pod2text $(ibdir)/pod2text
	cd ..
	rm -rf podlators-$(podlators-version)
	echo "podlators $(podlators-version)" > $@

# OpenSSL
#
# Until we find a nice and generic way to create an updated CA file in the
# project, the certificates will be available in a file for this project
# along with the other tarballs.
$(idir)/etc:; mkdir $@
$(idir)/etc/ssl: | $(idir)/etc; mkdir $@
$(ibidir)/openssl-$(openssl-version): $(ibidir)/podlators-$(podlators-version) \
                  | $(idir)/etc/ssl

#	First download the certificates and copy them into the
#	installation directory.
	tarball=cert.pem-$(certpem-version)
	$(call import-source, $(cert-url), $(cert-checksum))
	cp $(tdir)/cert.pem-$(certpem-version) $(idir)/etc/ssl/cert.pem

#	Now download the OpenSSL tarball.
	tarball=openssl-$(openssl-version).tar.lz
	$(call import-source, $(openssl-url), $(openssl-checksum))

#	According to OpenSSL's Wiki (link bellow), it can't automatically
#	detect Mac OS's structure. It will need some help. So we'll use the
#	'on_mac_os' Make variable that we defined in the configure script
#	and help it with some extra configuration options and an
#	environment variable.
#
#	https://wiki.openssl.org/index.php/Compilation_and_Installation
	if [ x$(on_mac_os) = xyes ]; then
	  export KERNEL_BITS=64
	  copt="shared no-ssl2 no-ssl3 enable-ec_nistp_64_gcc_128"
	fi
	$(call gbuild, openssl-$(openssl-version), , \
	               zlib \
	               $$copt \
	               $(rpath_command) \
	               --openssldir=$(idir)/etc/ssl \
	               --with-zlib-lib=$(ildir) \
	               --with-zlib-include=$(idir)/include, \
	               -j$(numthreads), , ./config )

#	Manually insert RPATH inside the two created libraries.
	if [ -f $(ibdir)/patchelf ]; then
	   patchelf --set-rpath $(ildir) $(ildir)/libssl.so
	   patchelf --set-rpath $(ildir) $(ildir)/libcrypto.so
	fi

#	Build the final target.
	echo "OpenSSL $(openssl-version)" > $@





# Downloaders
# -----------

# cURL
#
# cURL can optionally link with many different network-related libraries on
# the host system that we are not yet building in the template. Many of
# these are not relevant to most science projects, so we are explicitly
# using '--without-XXX' or '--disable-XXX' so cURL doesn't link with
# them. Note that if it does link with them, the configuration will crash
# when the library is updated/changed by the host, and the whole purpose of
# this project is avoid dependency on the host as much as possible.
$(ibidir)/curl-$(curl-version): $(ibidir)/coreutils-$(coreutils-version)

	tarball=curl-$(curl-version).tar.lz
	$(call import-source, $(curl-url), $(curl-checksum))

	$(call gbuild, curl-$(curl-version), , \
	               LIBS="-pthread" \
	               --with-zlib=$(ildir) \
	               --with-ssl=$(idir) \
	               --without-mesalink \
	               --with-ca-fallback \
	               --without-librtmp \
	               --without-libidn2 \
	               --without-wolfssl \
	               --without-nghttp2 \
	               --without-nghttp3 \
	               --without-brotli \
	               --without-gnutls \
	               --without-cyassl \
	               --without-libpsl \
	               --without-axtls \
	               --disable-ldaps \
	               --disable-ldap \
	               --without-zstd \
	               --without-nss, V=1)

	if [ -f $(ibdir)/patchelf ]; then
	   $(ibdir)/patchelf --set-rpath $(ildir) $(ildir)/libcurl.so
	fi
	echo "cURL $(curl-version)" > $@


# GNU Wget
#
# Note that on some systems (for example GNU/Linux) Wget needs to explicity
# link with 'libdl', but on others (for example Mac OS) it doesn't. We
# check this at configure time and define the 'needs_ldl' variable.
#
# Also note that since Wget needs to load outside libraries dynamically, it
# gives a segmentation fault when built statically.
#
# There are many network related libraries that we are currently not
# building as part of this project. So to avoid too much dependency on the
# host system (especially a crash when these libraries are updated on the
# host), they are disabled here.
$(ibidir)/wget-$(wget-version): \
               $(ibidir)/libiconv-$(libiconv-version) \
               $(ibidir)/coreutils-$(coreutils-version)

#	Download the tarball.
	tarball=wget-$(wget-version).tar.lz
	$(call import-source, $(wget-url), $(wget-checksum))

#	We need to explicitly disable 'libiconv', because of the
#	'pkg-config' and 'libiconv' problem.
	libs="-pthread"
	if [ x$(needs_ldl) = xyes ]; then libs="$$libs -ldl"; fi
	$(call gbuild, wget-$(wget-version), , \
	               LIBS="$$LIBS $$libs" \
	               --with-libssl-prefix=$(idir) \
	               --without-libiconv-prefix \
	               --with-ssl=openssl \
	               --with-openssl=yes \
	               --without-metalink \
	               --without-libuuid \
	               --without-libpsl \
	               --without-libidn \
	               --disable-pcre2 \
	               --disable-pcre \
	               --disable-iri, V=1 -j$(numthreads))
	echo "GNU Wget $(wget-version)" > $@







# Basic command-line tools and their dependencies
# -----------------------------------------------
#
# These are basic programs which are commonly necessary in the build
# process of the higher-level programs and libraries. Note that during the
# building of those higher-level programs (after this Makefile finishes),
# there is no access to the system's PATH.
$(ibidir)/bison-$(bison-version): $(ibidir)/help2man-$(help2man-version)
	tarball=bison-$(bison-version).tar.lz
	$(call import-source, $(bison-url), $(bison-checksum))
	$(call gbuild, bison-$(bison-version), static, ,V=1 -j$(numthreads))
	echo "GNU Bison $(bison-version)" > $@

$(ibidir)/diffutils-$(diffutils-version): \
                    $(ibidir)/coreutils-$(coreutils-version)
	tarball=diffutils-$(diffutils-version).tar.lz
	$(call import-source, $(diffutils-url), $(diffutils-checksum))
	$(call gbuild, diffutils-$(diffutils-version), static,,V=1)
	echo "GNU Diffutils $(diffutils-version)" > $@

$(ibidir)/file-$(file-version): $(ibidir)/coreutils-$(coreutils-version)
	export CFLAGS="-std=c99 $$CFLAGS"
	tarball=file-$(file-version).tar.lz
	$(call import-source, $(file-url), $(file-checksum))
	$(call gbuild, file-$(file-version), static, \
	               --disable-libseccomp, V=1)
	echo "File $(file-version)" > $@

$(ibidir)/findutils-$(findutils-version): \
                    $(ibidir)/coreutils-$(coreutils-version)
	tarball=findutils-$(findutils-version).tar.lz
	$(call import-source, $(findutils-url), $(findutils-checksum))
	$(call gbuild, findutils-$(findutils-version), static,,V=1)
	echo "GNU Findutils $(findutils-version)" > $@

$(ibidir)/gawk-$(gawk-version): \
               $(ibidir)/gmp-$(gmp-version) \
               $(ibidir)/mpfr-$(mpfr-version) \
               $(ibidir)/coreutils-$(coreutils-version)

#	Download the tarball.
	tarball=gawk-$(gawk-version).tar.lz
	$(call import-source, $(gawk-url), $(gawk-checksum))

#	AWK doesn't include RPATH by default, so we'll have to manually
#	include it using the 'patchelf' program (which was a dependency of
#	Bash). Just note that AWK produces two executables (for example
#	'gawk-4.2.1' and 'gawk') and a symbolic link 'awk' to one of those
#	executables.
	$(call gbuild, gawk-$(gawk-version), static, \
	               --with-readline=$(idir))

#	Correct the RPATH on systems that have installed patchelf.
	if [ -f $(ibdir)/patchelf ]; then
	  if [ -f $(ibdir)/gawk ]; then
	    $(ibdir)/patchelf --set-rpath $(ildir) $(ibdir)/gawk
	  fi
	  if [ -f $(ibdir)/gawk-$(gawk-version) ]; then
	    $(ibdir)/patchelf --set-rpath $(ildir) \
	                      $(ibdir)/gawk-$(gawk-version);
	  fi
	fi

#	Build final target.
	echo "GNU AWK $(gawk-version)" > $@

$(ibidir)/help2man-$(help2man-version): \
                   $(ibidir)/coreutils-$(coreutils-version)
	tarball=help2man-$(help2man-version).tar.lz
	$(call import-source, $(help2man-url), $(help2man-checksum))
	$(call gbuild, help2man-$(help2man-version), static, ,V=1)
	echo "Help2man $(Help2man-version)" > $@

$(ibidir)/libiconv-$(libiconv-version): \
                   $(ibidir)/pkg-config-$(pkgconfig-version)
	tarball=libiconv-$(libiconv-version).tar.lz
	$(call import-source, $(libiconv-url), $(libiconv-checksum))
	$(call gbuild, libiconv-$(libiconv-version), static)
	echo "GNU libiconv $(libiconv-version)" > $@

$(ibidir)/libunistring-$(libunistring-version): \
                       $(ibidir)/libiconv-$(libiconv-version)
	tarball=libunistring-$(libunistring-version).tar.lz
	$(call import-source, $(libunistring-url), $(libunistring-checksum))
	$(call gbuild, libunistring-$(libunistring-version), static,, \
	               -j$(numthreads))
	echo "GNU libunistring $(libunistring-version)" > $@

$(ibidir)/libxml2-$(libxml2-version): $(ibidir)/patchelf-$(patchelf-version)
#	The libxml2 tarball also contains Python bindings which are built
#	and installed to a system directory by default. If you don't need
#	the Python bindings, the easiest solution is to compile without
#	Python support: './configure --without-python'. If you really need
#	the Python bindings, use '--with-python-install-dir=DIR' instead.
	tarball=libxml2-$(libxml2-version).tar.lz
	$(call import-source, $(libxml2-url), $(libxml2-checksum))
	$(call gbuild, libxml2-$(libxml2-version), static, \
	               --without-python, V=1)
	echo "Libxml2 $(libxml2-version)" > $@

$(ibidir)/gettext-$(gettext-version): \
                  $(ibidir)/m4-$(m4-version) \
                  $(ibidir)/libxml2-$(libxml2-version) \
                  $(ibidir)/ncurses-$(ncurses-version) \
                  $(ibidir)/libiconv-$(libiconv-version) \
                  $(ibidir)/libunistring-$(libunistring-version)
	tarball=gettext-$(gettext-version).tar.lz
	$(call import-source, $(gettext-url), $(gettext-checksum))
	$(call gbuild, gettext-$(gettext-version), static, \
	               --without-emacs, V=1 -j$(numthreads))
	echo "GNU gettext $(gettext-version)" > $@

$(ibidir)/git-$(git-version): \
              $(ibidir)/less-$(less-version) \
              $(ibidir)/curl-$(curl-version) \
              $(ibidir)/gettext-$(gettext-version) \
              $(ibidir)/libiconv-$(libiconv-version)
	tarball=git-$(git-version).tar.lz
	if [ x$(on_mac_os) = xyes ]; then
	  export LDFLAGS="$$LDFLAGS -lcharset"
	fi
	$(call import-source, $(git-url), $(git-checksum))
	$(call gbuild, git-$(git-version), static, \
	               --without-tcltk --with-shell=$(ibdir)/bash \
	               --with-iconv=$(idir), V=1 -j$(numthreads))
	echo "Git $(git-version)" > $@

$(ibidir)/gmp-$(gmp-version): \
              $(ibidir)/m4-$(m4-version) \
              $(ibidir)/coreutils-$(coreutils-version)
	tarball=gmp-$(gmp-version).tar.lz
	$(call import-source, $(gmp-url), $(gmp-checksum))
	$(call gbuild, gmp-$(gmp-version), static, \
	               --enable-cxx --enable-fat, \
	               -j$(numthreads))
	echo "GNU Multiple Precision Arithmetic Library $(gmp-version)" > $@

# Less is useful with Git (to view the diffs within a minimal container)
# and generally to view large files easily when the project is built in a
# container with a minimal OS.
$(ibidir)/less-$(less-version): $(ibidir)/ncurses-$(ncurses-version)
	tarball=less-$(less-version).tar.lz
	$(call import-source, $(less-url), $(less-checksum))

#	Without the '--with-regex=posix' option, the build will depend on
#	PCRE (perl compatible regular expressions) which are not available
#	on some systems/compilers and can cause a crash. Maneage was
#	successfully built with the POSIX regular expression (regex), and
#	'less' is generally, an interactive software, not a batch-mode
#	software (it is just added in 'basic.mk' because Git uses it to
#	display things. Again, this is an interactive meta-operation in
#	maneage (operations you only do when you are developing Maneage
#	within Maneage interactively, and will not affect into the actual
#	reproducible analysis!)
	$(call gbuild, less-$(less-version), static, \
	               --with-regex=posix,-j$(numthreads))
	if [ -f $(ibdir)/patchelf ]; then
	  $(ibdir)/patchelf --set-rpath $(ildir) $(ibdir)/less;
	fi
	echo "Less $(less-version)" > $@

# On Mac OS, libtool does different things, so to avoid confusion, we'll
# prefix GNU's libtool executables with 'glibtool'.
$(ibidir)/libtool-$(libtool-version): $(ibidir)/m4-$(m4-version)
	tarball=libtool-$(libtool-version).tar.lz
	$(call import-source, $(libtool-url), $(libtool-checksum))
	$(call gbuild, libtool-$(libtool-version), static, \
                       --program-prefix=g, V=1 -j$(numthreads))
	ln -sf $(ibdir)/glibtoolize $(ibdir)/libtoolize
	echo "GNU Libtool $(libtool-version)" > $@

$(ibidir)/grep-$(grep-version): $(ibidir)/coreutils-$(coreutils-version)
	tarball=grep-$(grep-version).tar.lz
	$(call import-source, $(grep-url), $(grep-checksum))
	$(call gbuild, grep-$(grep-version), static,, \
	               -j$(numthreads) V=1)
	echo "GNU Grep $(grep-version)" > $@

# M4 doesn't depend on PatchELF, but just to be consistent with the
# levels/phases introduced here (where the compressors are level 1,
# PatchELF is level 2, and ...), we'll set it as a dependency.
#
# The '--with-syscmd-shell' is used as the default shell and if not given,
# 'm4' will use '/bin/sh' (which is not under Maneage control and can cause
# problems in 'high-level.mk' because it closes off the system's
# LD_LIBRARY_PATH and if the system's '/bin/sh' needs a special system
# library, the high-level programs will not be built). We are setting this
# default shell to Dash because M4 is built before our own Bash. Recall
# that Dash is built before we enter this Makefile.
$(ibidir)/m4-$(m4-version): $(ibidir)/patchelf-$(patchelf-version)
	tarball=m4-$(m4-version).tar.lz
	$(call import-source, $(m4-url), $(m4-checksum))
	$(call gbuild, m4-$(m4-version), static, \
	               --with-syscmd-shell=$(ibdir)/dash, \
	               -j$(numthreads) V=1)
	echo "GNU M4 $(m4-version)" > $@

$(ibidir)/mpfr-$(mpfr-version): $(ibidir)/gmp-$(gmp-version)
	tarball=mpfr-$(mpfr-version).tar.lz
	$(call import-source, $(mpfr-url), $(mpfr-checksum))
	$(call gbuild, mpfr-$(mpfr-version), static)
	echo "GNU Multiple Precision Floating-Point Reliably $(mpfr-version)" > $@

$(ibidir)/pkg-config-$(pkgconfig-version): $(ibidir)/patchelf-$(patchelf-version)

#	Download the tarball.
	tarball=pkg-config-$(pkgconfig-version).tar.lz
	$(call import-source, $(pkgconfig-url), $(pkgconfig-checksum))

#	An existing 'libiconv' can cause a conflict with 'pkg-config', this
#	is why 'libiconv' depends on 'pkg-config'. On a clean build,
#	'pkg-config' is built first. But when we don't have a clean build
#	(and 'libiconv' exists) there will be a problem. So before
#	re-building 'pkg-config', we'll remove any installation of
#	'libiconv'.
	rm -f $(ildir)/libiconv* $(idir)/include/iconv.h

#	Some Mac OS systems may have a version of the GNU C Compiler (GCC)
#	installed that doesn't support some necessary features of building
#	Glib (as part of pkg-config). So to be safe, for Mac systems, we'll
#	make sure it will use LLVM's Clang.
	if [ x$(on_mac_os) = xyes ]; then export compiler="CC=clang"
	else                              export compiler=""
	fi
	$(call gbuild, pkg-config-$(pkgconfig-version), static, \
	               $$compiler --with-internal-glib \
	               --with-pc-path=$(ildir)/pkgconfig, V=1)
	echo "pkg-config $(pkgconfig-version)" > $@

$(ibidir)/sed-$(sed-version): $(ibidir)/coreutils-$(coreutils-version)
	tarball=sed-$(sed-version).tar.lz
	$(call import-source, $(sed-url), $(sed-checksum))
	$(call gbuild, sed-$(sed-version), static,,V=1)
	echo "GNU Sed $(sed-version)" > $@

$(ibidir)/texinfo-$(texinfo-version): \
                  $(ibidir)/perl-$(perl-version) \
                  $(ibidir)/gettext-$(gettext-version)

#	Setting for the XS sub-package. "This is because in theory, the XS
#	module could be built with a different compiler to the rest of the
#	project, needing completely different flags" (part of [1])
#
#       [1] https://lists.gnu.org/archive/html/bug-texinfo/2022-08/msg00068.html
	export PERL="$(ibdir)/perl"
	export PERL_EXT_LDFLAGS="-L$(ildir)"
	export PERL_EXT_CPPFLAGS="-I$(iidir)"

#	Basic build commands.
	tarball=texinfo-$(texinfo-version).tar.lz
	$(call import-source, $(texinfo-url), $(texinfo-checksum))
	$(call gbuild, texinfo-$(texinfo-version), static)
	if [ -f $(ibdir)/patchelf ]; then
	  $(ibdir)/patchelf --set-rpath $(ildir) $(ibdir)/info
	  $(ibdir)/patchelf --set-rpath $(ildir) $(ibdir)/install-info
	fi
	echo "GNU Texinfo $(texinfo-version)" > $@

$(ibidir)/which-$(which-version): $(ibidir)/coreutils-$(coreutils-version)
	tarball=which-$(which-version).tar.lz
	$(call import-source, $(which-url), $(which-checksum))
	$(call gbuild, which-$(which-version), static)
	echo "GNU Which $(which-version)" > $@

# GNU ISL is necessary to build GCC.
$(ibidir)/isl-$(isl-version): $(ibidir)/gmp-$(gmp-version)
	tarball=isl-$(isl-version).tar.lz
	$(call import-source, $(isl-url), $(isl-checksum))
	if [ $(host_cc) = 1 ]; then
	  echo "" > $@
	else
	  $(call gbuild, isl-$(isl-version), static, , \
	                 V=1 -j$(numthreads))
	  echo "GNU Integer Set Library $(isl-version)" > $@
	fi

# GNU MPC is necessary to build GCC.
$(ibidir)/mpc-$(mpc-version): $(ibidir)/mpfr-$(mpfr-version)
	tarball=mpc-$(mpc-version).tar.lz
	$(call import-source, $(mpc-url), $(mpc-checksum))
	if [ $(host_cc) = 1 ]; then
	  echo "" > $@
	else
	  $(call gbuild, mpc-$(mpc-version), static, , \
	                 -j$(numthreads))
	  echo "GNU Multiple Precision Complex library" > $@
	fi










# Level 5: Binutils & GCC
# -----------------------
#
# The installation of Binutils can cause problems during the build of other
# programs since it provides the linker that is used to build them
# (http://savannah.nongnu.org/bugs/?56294). However, it is necessary for
# GCC. Therefore, we'll set all other basic programs as Binutils
# prerequisites, so GCC (the almost-final basic target) ultimately just
# depends on Binutils.
$(ibidir)/binutils-$(binutils-version): \
                   $(ibidir)/git-$(git-version) \
                   $(ibidir)/isl-$(isl-version) \
                   $(ibidir)/mpc-$(mpc-version) \
                   $(ibidir)/sed-$(sed-version) \
                   $(ibidir)/file-$(file-version) \
                   $(ibidir)/gawk-$(gawk-version) \
                   $(ibidir)/grep-$(grep-version) \
                   $(ibidir)/wget-$(wget-version) \
                   $(ibidir)/bison-$(bison-version) \
                   $(ibidir)/which-$(which-version) \
                   $(ibidir)/libtool-$(libtool-version) \
                   $(ibidir)/texinfo-$(texinfo-version) \
                   $(ibidir)/coreutils-$(coreutils-version) \
                   $(ibidir)/diffutils-$(diffutils-version) \
                   $(ibidir)/findutils-$(findutils-version)

#	Download the tarball.
	tarball=binutils-$(binutils-version).tar.lz
	$(call import-source, $(binutils-url), $(binutils-checksum))

#	Binutils' assembler ('as') and linker ('ld') will conflict with
#	other compilers. So if we don't build our own compiler, we'll use
#	the host opertating system's equivalents by just making links.
	if [ x$(on_mac_os) = xyes ]; then
	  $(call makelink,as)
	  $(call makelink,ar)
	  $(call makelink,ld)
	  $(call makelink,nm)
	  $(call makelink,ps)
	  $(call makelink,strip)
	  $(call makelink,ranlib)
	  echo "" > $@
	else

#	  Build binutils with the standard 'gbuild' function.
	  $(call gbuild, binutils-$(binutils-version), static, \
	                 --with-lib-path=$(sys_library_path), \
	                 -j$(numthreads) V=1)

#	  The 'ld' linker of Binutils needs several '*crt*.o' files from
#	  the host's GNU C Library to run. On some systems these object
#	  files aren't installed in standard places. We defined
#	  'LIBRARY_PATH' and that fixed the problem for many
#	  systems. However, some software (for example ImageMagick)
#	  over-write 'LIBRARY_PATH', therefore there is no other way than
#	  to put a link to these necessary files in our local build
#	  directory. IMPORTANT NOTE: later, when we build the GNU C Library
#	  in the project, we should remove this step.
	  if ! [ x"$(sys_library_path)" = x ]; then
	    for f in $(sys_library_path)/*crt*.o; do
	      b=$$($(ibdir)/basename $$f)
	      ln -sf $$f $(ildir)/$$b
	    done
	  fi

#	  Write the final target.
	  echo "GNU Binutils $(binutils-version)" > $@
	fi

# We are having issues with 'libiberty' (part of GCC) on Mac. So for now,
# GCC won't be built there. Since almost no natural science paper's
# processing depends so strongly on the compiler used, for now, this isn't
# a bad assumption, but we are indeed searching for a solution.
#
# Based on the GCC manual, the GCC build can benefit from a GNU
# environment. So, we'll build GCC after building all the basic tools that
# are often used in a configure and build scripts of GCC components.
#
# Objective C and Objective C++ is necessary for installing 'matplotlib'.
#
# We are currently having problems installing GCC on macOS, so for the time
# being, if the project is being run on a macOS, we'll just set a link.
$(ibidir)/gcc-$(gcc-version): $(ibidir)/binutils-$(binutils-version)

#	Function to let the users know what to do if build fails.
	error_message() {
	    echo; echo
	    echo "_________________________________________________"
	    echo "!!!!!!!!       Warning from Maneage      !!!!!!!!"
	    echo
	    echo "Unfortunately building of GCC failed on this system!"
	    echo "Can you please copy the last ~500 lines above and post it"
	    echo "as a bug here (as an attached file):"
	    echo "  https://sv.nongnu.org/support/?func=additem&group=reproduce"
	    echo
	    echo "In the meantime, please re-configure Maneage with '--host-cc'"
	    echo "like below so it uses your own C compiler for building the"
	    echo "high-level software ('-e' is to use the existing configuration):"
	    echo
	    echo "  ./project configure -e --host-cc"
	    echo
	    echo "__________ SEE NOTE FROM MANEAGE ABOVE __________"
	    echo; exit 1
	}

#	Download the tarball.
	tarball=gcc-$(gcc-version).tar.lz
	$(call import-source, $(gcc-url), $(gcc-checksum))

#	To avoid any previous build in '.local/bin' causing problems in
#	this build/links of this GCC, we'll first delete all the possibly
#	built/existing compilers in this project. Note that GCC also
#	installs several executables like this 'x86_64-pc-linux-gnu-gcc',
#	'x86_64-pc-linux-gnu-gcc-ar' or 'x86_64-pc-linux-gnu-g++'.
	rm -f $(ibdir)/*g++ $(ibdir)/cpp $(ibdir)/gfortran
	rm -rf $(ildir)/gcc $(ildir)/libcc* $(ildir)/libgcc*
	rm -f $(ibdir)/*gcc* $(ibdir)/gcov* $(ibdir)/cc $(ibdir)/c++
	rm -rf $(ildir)/libgfortran* $(ildir)/libstdc* rm $(idir)/x86_64*

#	Build (or set links) to GCC.
	if [ $(host_cc) = 1 ]; then

#	  Put links to the host's tools in '.local/bin'. Note that some
#	  macOS systems have both a native clang *and* a GNU C Compiler
#	  (note that this is different from the "normal" macOS situation
#	  where 'gcc' actually points to clang, here we mean when 'gcc' is
#	  actually the GNU C Compiler).
#
#	  In such cases, the GCC isn't complete and using it will cause
#	  problems when building high-level tools (for example openBLAS,
#	  rpcsvc-proto, CMake, xlsxio, Python or Matplotlib among
#	  others). To avoid such situations macOSs are configured like
#	  this: we'll simply set 'gcc' to point to 'clang' and won't set
#	  'gcc' to point to the system's 'gcc'.
#
#	  Also, note that LLVM's clang doesn't have a C Pre-Processor. So
#	  we will only put a link to the host's 'cpp' if the system is not
#	  macOS. On macOS systems that have a real GCC installed, having
#	  GNU CPP in the project build directory is known to cause problems
#	  with 'libX11'.
	  $(call makelink,gfortran)
	  if [ x$(on_mac_os) = xyes ]; then
	    $(call makelink,clang)
	    $(call makelink,clang++)
	    $(call makelink,clang,,gcc)
	    $(call makelink,clang++,,g++)
	  else
	    $(call makelink,cpp)
	    $(call makelink,gcc)
	    $(call makelink,g++)
	  fi

#	  We also want to have the two 'cc' and 'c++' in the build
#	  directory that point to the selected compiler. With the checks
#	  above, 'gcc' and 'g++' will point to the proper compiler, so
#	  we'll use them to define 'cc' and 'c++'.
	  $(call makelink,gcc,,cc)
	  $(call makelink,g++,,c++)

#	  Get the first line of the compiler's '--version' output and put
#	  that into the target (so we know want compiler was used).
	  ccinfo=$$(gcc --version | awk 'NR==1')
	  echo "C compiler (""$$ccinfo"")" > $@

	else

#	  Mark the current directory.
	  current_dir=$$(pwd)

#	  By default 'ddir' (where GCC is decompressed and built) is in the
#	  RAM (on systems that support a '/dev/shm' RAM disk). This is done
#	  to avoid building so many small/temporary files and possibly
#	  harming the hard-drive or SSD. But if the RAM doesn't have enough
#	  space, we should use the hard-drive or SSD. During its build,
#	  GCC's build directory will become about 7GiB (in units of 1024^3
#	  bytes, for GCC 12.1.0, which corresponds to 7.5GB, in units of
#	  1000^3 bytes). So at this step, we make sure that we have more
#	  than 12GiB before GCC starts to build. See the figure in the link
#	  below for GCC's RAM consumption as a function of time:
#
#	  https://savannah.nongnu.org/task/?16244#comment12
#
#	  For POSIX portability and longevity (default sizes might change),
#	  we use the '-P' option, and we use the environment variable
#	  POSIXLY_CORRECT=1, so the 'block size' is 512 bytes. We'll also
#	  allow for about ~0.5 GB at the start.
#
#	  So we need 8 GiB * 1024^3 (B/GiB) / 512 blocks/B = 16777216
#	  blocks, in blocks of 512 bytes.
#
#	  The 4th column of 'df' is the "available" space at the time of
#	  running, not the full space. So the 'RAM disk' that the OS
#	  will be using as "pretend" disk space (e.g. using 'tmpfs'; this
#	  is physically RAM, but appears as if it is disk space)
#	  during this stage of Maneage is accounted for. GCC is built
#	  alone - no other Maneage software is built at the same time as
#	  GCC - so this amount of RAM should be enough.
	  in_ram=$$(POSIXLY_CORRECT=1 df -P $(ddir) \
	               | awk 'NR==2{print ($$4>16777216) ? "yes" : "no"}'); \
	  if [ $$in_ram = "yes" ]; then odir=$(ddir)
	  else
	    odir=$(BDIR)/software/build-tmp-gcc-due-to-lack-of-space
	    if [ -d $$odir ]; then rm -rf $$odir; fi
	    mkdir $$odir
	  fi

#	  Go into the directory to uncompress GCC.
	  cd $$odir

#	  Unpack GCC and prepare the 'build' directory inside it for all
#	  the built files.
	  rm -rf gcc-$(gcc-version)
	  tar -xf $(tdir)/$$tarball
	  if [ $$odir != $(ddir) ]; then
	    ln -s $$odir/gcc-$(gcc-version) $(ddir)/gcc-$(gcc-version)
	  fi
	  cd gcc-$(gcc-version)
	  $(shsrcdir)/prep-source.sh $(ibdir)

#	  Unfortunately binutils installs headers like 'ansidecl.h' that
#	  have been seen to conflict with GCC's internal versions of those
#	  headers. For example in the 'ansidecl.h' of Binutils 2.39, the
#	  'PTR' macro isn't defined, while the same file in GCC 12.1.0 has
#	  defined it. Therefore, without this change, GCC will include the
#	  file installed from Binutils, not find what it needs and crash!
#	  Therefore, with the 'CPPFLAGS' modification below, we tell GCC to
#	  first look into its own 'include' directory before anything else.
	  export CPPFLAGS="-I$$(pwd)/include $(CPPFLAGS)"

#	  In the GNU C Library 2.36 (which is more recent than GCC 12.1.0),
#	  the 'linux/mount.h' (loaded by 'linux/fs.h', which is loaded by
#	  'libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp'
#	  in GCC) conflicts with 'sys/mount.h' which is directly loaded by
#	  the same file! This is a known conflict in glibc 2.36 (see
#	  [1]). As described in [1], one solution is the final job done in
#	  [2]. We therefore do this process here: 1) Not loading
#	  'linux/fs.h', and adding the necessary macros directly.
#
#	  [1] https://sourceware.org/glibc/wiki/Release/2.36#Usage_of_.3Clinux.2Fmount.h.3E_and_.3Csys.2Fmount.h.3E
#         [2] https://reviews.llvm.org/D129471
	  sed -e's|\#include <linux/fs.h>||' \
	      -e"s|FS_IOC_GETFLAGS;|_IOR('f', 1, long);|" \
	      -e"s|FS_IOC_GETVERSION;|_IOR('v', 1, long);|" \
	      -e"s|FS_IOC_SETFLAGS;|_IOW('f', 2, long);|" \
	      -e"s|FS_IOC_SETVERSION;|_IOW('v', 2, long);|" \
	      -i libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp

#	  Set the build directory for the processing.
	  mkdir build
	  cd build

#	  Configure, build and install GCC, if any of three steps fails,
#	  the error message will be printed.
	  if ! ../configure SHELL=$(ibdir)/bash \
	                    --prefix=$(idir) \
	                    --with-mpc=$(idir) \
	                    --with-gmp=$(idir) \
	                    --with-isl=$(idir) \
	                    --with-mpfr=$(idir) \
	                    --with-local-prefix=$(idir) \
	                    --with-build-time-tools=$(idir) \
	                    --enable-lto \
	                    --enable-shared \
	                    --enable-cet=auto \
	                    --enable-default-pie \
	                    --enable-default-ssp \
	                    --enable-decimal-float \
	                    --enable-threads=posix \
	                    --enable-languages=c,c++,fortran,objc,obj-c++ \
	                    --disable-nls \
	                    --disable-libada \
	                    --disable-multilib; then error_message; fi
	  if ! make SHELL=$(ibdir)/bash -j$(numthreads); then error_message; fi
	  if ! make SHELL=$(ibdir)/bash install; then error_message; fi

#	  We need to manually fix the RPATH inside GCC's libraries, the
#	  programs built by GCC already have RPATH.
	  tempname=$$odir/gcc-$(gcc-version)/build/rpath-temp-copy
	  if [ -f $(ibdir)/patchelf ]; then

#	    Go over all the installed GCC libraries (its executables are
#	    fine!).
	    for f in $$(find $(idir)/libexec/gcc -type f) $(ildir)/libstdc++*; do

#	      Make sure this is a static library, copy it to a temporary
#	      name (to avoid any possible usage of the file while it is
#	      being corrected), and add RPATH inside of it and put the
#	      corrected file back in its place. In the case of the standard
#	      C++ library, we also need to manually insert a linking to
#	      libiconv.
	      if file $$f | grep -q "dynamically linked"; then
	        cp $$f $$tempname
	        patchelf --set-rpath $(ildir) $$tempname
	        echo "$$f: added rpath"
	        if echo $$f | grep -q "libstdc++"; then
	          patchelf --add-needed $(ildir)/libiconv.so $$tempname
	          echo "$$f: linked with libiconv"
	        fi
	        mv $$tempname $$f
	      fi
	    done
	  fi

#	  Come back up to the un-packing directory and delete the GCC
#	  source directory.
	  cd ../..
	  rm -rf gcc-$(gcc-version)
	  cd $$current_dir
	  if [ "$$odir" != "$(ddir)" ]; then
	    rm -rf $$odir;
	    rm $(ddir)/gcc-$(gcc-version);
	  fi

#	  Set 'cc' to point to 'gcc'.
	  ln -sf $(ibdir)/gcc $(ibdir)/cc
	  ln -sf $(ibdir)/g++ $(ibdir)/c++

#	  Write the final target.
	  echo "GNU Compiler Collection (GCC) $(gcc-version)" > $@
	fi





# Level 6: need re-compilation
# ----------------------------
#
# The initial build of these was done with the host's settings, which will
# cause problems later when we completely close-off the host environment.
$(ibidir)/make-$(make-version): $(ibidir)/gcc-$(gcc-version)
	tarball=make-$(make-version).tar.lz
	$(call import-source, $(make-url), $(make-checksum))
	$(call gbuild, make-$(make-version), static, \
	               --disable-dependency-tracking --without-guile)
	echo "GNU Make $(make-version)" > $@

$(ibidir)/lzip-$(lzip-version): $(ibidir)/gcc-$(gcc-version)
	tarball=lzip-$(lzip-version).tar
	unpackdir=lzip-$(lzip-version)
	cd $(ddir)
	rm -rf $$unpackdir
	tar -xf $(tdir)/$$tarball
	cd $$unpackdir
	$(shsrcdir)/prep-source.sh $(ibdir)
	./configure --build --check --installdir="$(ibdir)"
	if [ -f $(ibdir)/patchelf ]; then
	  $(ibdir)/patchelf --set-rpath $(ildir) $(ibdir)/lzip;
	fi
	cd ..
	rm -rf $$unpackdir
	echo "Lzip $(lzip-version)" > $@





# Level 7: Basic text editor
# --------------------------
#
# If the project is built in a minimal environment, there is no text
# editor, making it hard to work on the project. By default a minimal
# (relatively user-friendly: GNU Nano) text editor will thus also be built
# at the end of the "basic" tools. More advanced editors (for example Emacs
# and Vim) are available as optional high-level programs. GNU Nano is a
# very light-weight and small command-line text editor (around 3.5 Mb after
# installation!).
#
# The editor is a top-level target in the basic tools (given to
# 'targets-proglib' above). Hence nothing depends on it, and it just
# depends on GCC. This is done because some projects may choose to not have
# nano (and use their own optional high-level text editor). To do this, you
# can just have to manually remove 'nano' from 'targets-proglib' above and
# add their optional text editor in 'TARGETS.conf'.
$(ibidir)/nano-$(nano-version): $(ibidir)/lzip-$(lzip-version) \
                                $(ibidir)/make-$(make-version)
	tarball=nano-$(nano-version).tar.lz
	$(call import-source, $(nano-url), $(nano-checksum))
	$(call gbuild, nano-$(nano-version), static)
	echo "GNU Nano $(nano-version)" > $@