diff --git a/README.md b/README.md index c5ca76a1fec..7fb6eb4c9ec 100644 --- a/README.md +++ b/README.md @@ -344,6 +344,62 @@ For more details, see: http://doc.sagemath.org/html/en/developer/coding_basics.html#files-and-directory-structure +Build System +------------ + +This is a brief summary of the Sage software distribution's build system. +There are two components to the full Sage system--the Sage Python library +and its associated user interfaces, and the larger software distribution of +Sage's main dependencies (for those dependencies not supplied by the user's +system). + +Sage's Python library is built and installed using a `setup.py` script as is +standard for Python packages (Sage's `setup.py` is non-trivial, but not +unusual). + +Most of the rest of the build system is concerned with building all of Sage's +dependencies in the correct order in relation to each other. The dependencies +included by Sage are referred to as SPKGs (i.e. "Sage Packages") and are listed +under `build/pkgs`. + +The main entrypoint to Sage's build system is the top-level `Makefile` at the +root of the source tree. Unlike most normal projects that use autoconf (Sage +does as well, as described below), this `Makefile` is not generated. Instead, +it contains a few high-level targets and targets related to bootstrapping the +system. Nonetheless, we still run `make ` from the root of the source +tree--targets not explicitly defined in the top-level `Makefile` are passed +through to another Makefile under `build/make/Makefile`. + +The latter `build/make/Makefile` *is* generated by an autoconf-generated +`configure` script, using the template in `build/make/Makefile.in`. This +includes rules for building the Sage library itself (`make sagelib`), and for +building and installing each of Sage's dependencies (e.g. `make python2`). + +Although it's possible to manually run Sage's `configure` script if one wants +to provide some customizations (e.g. it is possible to select which BLAS +implementation to use), the top-level `Makefile` will run `configure` for you, +in order to build `build/make/Makefile` since it's a prerequisite for most of +Sage's make targets. + +The `configure` script itself, if it is not already built, can be generated by +running the `bootstrap` script. The top-level `Makefile` also takes care of +this automatically. + +To summarize, running a command like `make python2` at the top-level of the +source tree goes something like this: + +1. `make python2` +2. run `./bootstrap` if `configure` does not exist +3. run `./configure` if `build/make/Makefile` doe not exist +4. `cd` into `build/make` and run the `install` script--this is little more + than a front-end to running `make -f build/make/Makefile python2`, which + sets some necessary environment variables and logs some information +5. `build/make/Makefile` contains the actual rule for building `python2`; this + includes building all of `python2`'s dependencies first (and their + dependencies, recursively); the actual package installation is performed + with the `sage-spkg` program + + Relocation ---------- diff --git a/build/make/Makefile.in b/build/make/Makefile.in new file mode 100644 index 00000000000..7e94188cb4e --- /dev/null +++ b/build/make/Makefile.in @@ -0,0 +1,258 @@ +# Makefile template for Sage packages: This Makefile is filled by the +# ./configure script with information all of Sage's dependent packages (SPKGs), +# including their names, their current versions, their dependencies, and some +# classifications according to their installation priority ("standard", +# "optional") and installation method ("normal", "pip", "script"). +# +# Finally, install and clean rules for each package are generated from the +# templates at the end of this file. Because the templates may slightly +# obscure the substance of the actual rules, this file can be debugged by +# running: +# +# $ make -f build/make/Makefile -n DEBUG_RULES=1 +# +# This will not actually run any rules (the -n flag) but will print all the +# rules generated from the templates. + +# Always use bash for make rules +SHELL = @SHELL@ + +ifndef SAGE_SPKG_INST +ifndef DEBUG_RULES +$(error This Makefile needs to be invoked by build/make/install) +else +# A dummy value for this variable for debugging purposes +SAGE_SPKG_INST=installed +endif +endif + +# Directory to keep track of which packages are installed +INST = $(SAGE_SPKG_INST) + +# Aliases for optional packages selected at configure time +TOOLCHAIN = @SAGE_TOOLCHAIN@ +PYTHON = python@SAGE_PYTHON_VERSION@ +MP_LIBRARY = @SAGE_MP_LIBRARY@ +BLAS = @SAGE_BLAS@ + +# Files to track installation of packages +BUILT_PACKAGES = @SAGE_BUILT_PACKAGES@ +DUMMY_PACKAGES = @SAGE_DUMMY_PACKAGES@ + +# Versions of all the packages, in the format +# +# vers_ = + +@SAGE_PACKAGE_VERSIONS@ + +# Dependencies for all packages, in the format +# +# deps_ = etc... + +@SAGE_PACKAGE_DEPENDENCIES@ + +# All standard packages +STANDARD_PACKAGES = @SAGE_STANDARD_PACKAGES@ +STANDARD_PACKAGE_INSTS = \ + $(foreach pkgname,$(STANDARD_PACKAGES),$(inst_$(pkgname))) + +# All optional installed packages (triggers the auto-update) +OPTIONAL_INSTALLED_PACKAGES = @SAGE_OPTIONAL_INSTALLED_PACKAGES@ +OPTIONAL_INSTALLED_PACKAGE_INSTS = \ + $(foreach pkgname,$(OPTIONAL_INSTALLED_PACKAGES),$(inst_$(pkgname))) + +# All packages which should be downloaded +SDIST_PACKAGES = @SAGE_SDIST_PACKAGES@ +SDIST_PACKAGE_INSTS = $(foreach pkgname,$(SDIST_PACKAGES),$(inst_$(pkgname))) + +SCRIPTS = @SAGE_SCRIPTS@ + +EXTCODE = @SAGE_EXTCODE@ + +# Packages that use the 'normal' build rules +NORMAL_PACKAGES = @SAGE_NORMAL_PACKAGES@ + +# Packages that use the 'pip' package build rules +PIP_PACKAGES = @SAGE_PIP_PACKAGES@ + +# Packages that use the 'script' package build rules (not to be confused with +# the $(SCRIPTS) list) +SCRIPT_PACKAGES = @SAGE_SCRIPT_PACKAGES@ + + + +# Generate the actual inst_ variables; for each package that is +# actually built this generates a line like: +# +# inst_ = $(INST)/- +# +# And for 'dummy' package that are not actually built/installed (e.g. because +# configure determined we can use the package from the system): +# +# inst_ = $(INST)/.dummy +# +# For example: +# +# inst_python2 = $(INST)/python2-$(vers_python2) +# +# inst_git = $(INST)/.dummy + +$(foreach pkgname,$(BUILT_PACKAGES),\ + $(eval inst_$(pkgname) = $$(INST)/$(pkgname)-$(vers_$(pkgname)))) + +$(foreach pkgname,$(DUMMY_PACKAGES),\ + $(eval inst_$(pkgname) = $$(INST)/.dummy)) + + +# Dummy target for packages which are not installed +$(INST)/.dummy: + touch $@ + + +@SAGE_MAKE_DEPS@ + + +#============================================================================== +# Rules generated from pkgs//dependencies files +#============================================================================== + +# Define a function for generating the list of a package's dependencies +# as $(inst_) variables. For example, takes: +# +# deps_cysignals = python2 cython pari | pip +# +# to: +# +# $(inst_python2) $(inst_cython) $(inst_pari) | $(inst_pip) +# +# Positional arguments: +# $(1): package name +pkg_deps = \ + $(foreach dep,$(deps_$(1)),\ + $(if $(findstring |,$(dep)),|,$$(inst_$(dep)))) + +# ============================= normal packages ============================== +# Generate build rules for 'normal' packages; this template is used to generate +# three rules in the form: +# +# $(INST)/-: +# +$(AM_V_at)sage-logger -p '$(SAGE_SPKG) -' '$(SAGE_LOGS)/-.log' +# +# : $(INST)/- +# +# -clean: +# rm -rf $(INST)/- +# +# For example, for python2 this will expand to: +# +# $(INST)/python2-2.7.14: $(inst_zlib) $(inst_readline) $(inst_sqlite) $(inst_libpng) $(inst_bzip2) +# +$(AM_V_at)sage-logger -p '$(SAGE_SPKG) python2-2.7.14' '$(SAGE_LOGS)/python2-2.7.14.log' +# +# python2: $(INST)/python2-2.7.14 +# +# python2-clean: +# rm -rf $(INST)/python2-2.7.14 +# +# Note: In these rules the $(INST)/- target is used +# explicitly, rather than expanding the $(inst_) variable, since +# it may expand to $(INST)/.dummy for packages that were not configured +# for installation by default. However, we wish to be able to manually +# install those packages later. + +# Positional arguments: +# $(1): package name +# $(2): package version +# $(3): package dependencies +define NORMAL_PACKAGE_templ +$$(INST)/$(1)-$(2): $(3) + +$(AM_V_at)sage-logger -p '$$(SAGE_SPKG) $(1)-$(2)' '$$(SAGE_LOGS)/$(1)-$(2).log' + +$(1): $$(INST)/$(1)-$(2) + +$(1)-clean: + rm -rf $$(INST)/$(1)-$(2) + +endef + +$(foreach pkgname, $(NORMAL_PACKAGES),\ + $(eval $(call NORMAL_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),\ + $(call pkg_deps,$(pkgname))))) + +ifdef DEBUG_RULES +$(info # Rules for standard packages) +$(foreach pkgname, $(NORMAL_PACKAGES),\ + $(info $(call NORMAL_PACKAGE_templ,$(pkgname),$(vers_$(pkgname)),\ + $(call pkg_deps,$(pkgname))))) +endif + +# ================================ pip packages =============================== +# Generate build rules for 'pip' packages; this template is used to generate +# two rules in the form: +# +# : +# $(AM_V_at)sage-logger -p 'sage --pip install ' '$(SAGE_LOGS)/.log' +# +# -clean: +# -sage --pip uninstall -y + +# Positional arguments: +# $(1): package name +# $(2): package dependencies +define PIP_PACKAGE_templ +$(1): $(2) + $(AM_V_at)sage-logger -p 'sage --pip install $(1)' '$$(SAGE_LOGS)/$(1).log' + +$(1)-clean: + -sage --pip uninstall -y $(1) +endef + +$(foreach pkgname,$(PIP_PACKAGES),\ + $(eval $(call PIP_PACKAGE_templ,$(pkgname),$(call pkg_deps,$(pkgname))))) + +ifdef DEBUG_RULES +$(info # Rules for pip packages) +$(foreach pkgname,$(PIP_PACKAGES),\ + $(info $(call PIP_PACKAGE_templ,$(pkgname),$(call pkg_deps,$(pkgname))))) +endif + +# ============================= script packages ============================== +# Generate build rules for 'script' packages; this template is used to generate +# two rules in the form: +# +# : +# $(AM_V_at)cd '$SAGE_ROOT' && \\ +# source '$SAGE_ROOT/src/bin/sage-env' && \\ +# sage-logger -p '$SAGE_ROOT/build/pkgs//spkg-install' '$(SAGE_LOGS)/.log' +# +# -clean: +# -$(AM_V_at)cd '$SAGE_ROOT' && \\ +# source '$SAGE_ROOT/src/bin/sage-env' && \\ +# '$SAGE_ROOT/build/pkgs/$PKG_NAME/spkg-uninstall' + +# Positional arguments: +# $(1): package name +# $(2): package dependencies +define SCRIPT_PACKAGE_templ +$(1): $(2) + $(AM_V_at)cd '$$(SAGE_ROOT)' && \ + source '$$(SAGE_ROOT)/src/bin/sage-env' && \ + sage-logger -p '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-install' '$$(SAGE_LOGS)/$(1).log' + +$(1)-clean: + -$(AM_V_at)cd '$$(SAGE_ROOT)' && \ + source '$$(SAGE_ROOT)/src/bin/sage-env' && \ + '$$(SAGE_ROOT)/build/pkgs/$(1)/spkg-uninstall' +endef + +$(foreach pkgname,$(SCRIPT_PACKAGES),\ + $(eval $(call SCRIPT_PACKAGE_templ,$(pkgname),$(call pkg_deps,$(pkgname))))) + +ifdef DEBUG_RULES +$(info # Rules for script packages) +$(foreach pkgname,$(SCRIPT_PACKAGES),\ + $(info $(call SCRIPT_PACKAGE_templ,$(pkgname),$(call pkg_deps,$(pkgname))))) +endif + +.PHONY: $(NORMAL_PACKAGES) $(addsuffix -clean,$(NORMAL_PACKAGES)) \ + $(PIP_PACKAGES) $(addsuffix -clean,$(PIP_PACKAGES)) \ + $(SCRIPT_PACKAGES) $(addsuffix -clean,$(SCRIPT_PACKAGES)) diff --git a/build/make/deps b/build/make/deps index ca5418a83a1..20476126718 100644 --- a/build/make/deps +++ b/build/make/deps @@ -1,6 +1,6 @@ ## -*- Makefile -*- ########################################################### # This file ($SAGE_ROOT/build/make/deps) will be copied into -# $SAGE_ROOT/build/make/Makefile by $SAGE_ROOT/build/make/install +# $SAGE_ROOT/build/make/Makefile by config.status ############################################################################### # Silent rules @@ -49,8 +49,8 @@ all-toolchain: # All targets except for the base packages all-sage: \ sagelib \ - $(STANDARD_PACKAGES) \ - $(OPTIONAL_INSTALLED_PACKAGES) \ + $(STANDARD_PACKAGE_INSTS) \ + $(OPTIONAL_INSTALLED_PACKAGE_INSTS) \ $(EXTCODE) \ $(SCRIPTS) @@ -58,11 +58,11 @@ all-sage: \ # option to make forces all targets to be built unconditionally) download-for-sdist: base env SAGE_INSTALL_FETCH_ONLY=yes $(MAKE) -B SAGERUNTIME= \ - $(SDIST_PACKAGES) + $(SDIST_PACKAGE_INSTS) # TOOLCHAIN consists of dependencies determined by configure. # These are built after the "base" target but before anything else. -toolchain: $(TOOLCHAIN) +toolchain: $(foreach pkgname,$(TOOLCHAIN),$(inst_$(pkgname))) # Build all packages that GCC links against serially, otherwise this # leads to race conditions where some library which is used by GCC gets @@ -93,7 +93,7 @@ start: all-build # We make this depend on all standard packages because running # sage-starts runs sage-location, which should be run after installing # any package. -$(STARTED): $(STANDARD_PACKAGES) +$(STARTED): $(STANDARD_PACKAGE_INSTS) $(AM_V_at)"$(SAGE_LOCAL)/bin/sage-starts" diff --git a/build/pkgs/gcc/spkg-install b/build/pkgs/gcc/spkg-install index 07a49a90c2a..2f8a0415f18 100644 --- a/build/pkgs/gcc/spkg-install +++ b/build/pkgs/gcc/spkg-install @@ -124,7 +124,7 @@ $MAKE install # Mark gfortran as not installed in case gfortran was installed earlier cd "$SAGE_SPKG_INST" -rm -f gmp-* mpir-* mpfr-* mpc-* gfortran-* +rm -f gfortran-* # Force re-configuration: the next time that "make" is run, we need to # rebuild all packages (#24703) but we should not rebuild gcc (#19324) diff --git a/configure.ac b/configure.ac index 8439609b97e..4666e3d4bd3 100644 --- a/configure.ac +++ b/configure.ac @@ -668,33 +668,9 @@ if test x$need_to_install_gcc = xyes -o x$SAGE_INSTALL_GCC = xexists ; then fi ############################################################################### -# Create $SAGE_ROOT/build/make/Makefile starting from build/make/deps +# Collect substitutions for build/make/Makefile.in ############################################################################### -# Use file descriptor 7 since make uses 3 and 4 and configure uses 5 and 6 -exec 7>build/make/Makefile - -cat >&7 <&7 "PYTHON = \$(inst_python${SAGE_PYTHON_VERSION})" - # Sage MP library AC_MSG_CHECKING([multiprecision library]) @@ -736,9 +710,9 @@ esac AC_MSG_RESULT([$with_mp]) if test $with_mp = MPIR; then - echo >&7 'MP_LIBRARY = $(inst_mpir)' + AC_SUBST([SAGE_MP_LIBRARY], [mpir]) else - echo >&7 'MP_LIBRARY = $(inst_gmp)' + AC_SUBST([SAGE_MP_LIBRARY], [gmp]) fi # BLAS library @@ -758,309 +732,48 @@ case "$with_blas" in esac AC_MSG_RESULT([$with_blas]) - -echo >&7 "BLAS = \$(inst_${with_blas})" +AC_SUBST([SAGE_BLAS], [$with_blas]) # $(TOOLCHAIN) variable containing prerequisites for the build -AS_ECHO_N(>&7 ['TOOLCHAIN = $(inst_gcc)']) +SAGE_TOOLCHAIN=gcc if test "$SAGE_INSTALL_CCACHE" = yes ; then - AS_ECHO_N(>&7 [' $(inst_ccache)']) + SAGE_TOOLCHAIN="$SAGE_TOOLCHAIN ccache" fi -echo >&7 - -AC_MSG_CHECKING([package versions]) -AC_MSG_RESULT([]) - -# Usage: newest_version $pkg -# Print version number of latest package $pkg -newest_version() { - PKG=$1 - if test -f "$SAGE_ROOT/build/pkgs/$PKG/package-version.txt" ; then - AS_ECHO_N(["$PKG-"]) - cat "$SAGE_ROOT/build/pkgs/$PKG/package-version.txt" - else - echo "$PKG" - fi -} - -# Outputs the list of packages, filtered by 'type', e.g.: -# -# filtered_packages_list base -# filtered_packages_list standard -# filtered_packages_list optional -# filtered_packages_list experimental -# -# Or, if you want all packages: -# -# filtered_packages_list all -# -# Or, if you want all packages which should appear in the source tarball: -# -# filtered_packages_list sdist -# -# The output consists of triples -# PKG_NAME PKG_VERSION PKG_VAR -# - -changequote(<,>) -filtered_packages_list() { - # for each package in pkgs/ - for DIR in $SAGE_ROOT/build/pkgs/*; do - test -d "$DIR" || continue - - PKG_TYPE_FILE="$DIR/type" - if [ -f "$PKG_TYPE_FILE" ]; then - PKG_TYPE=`cat $PKG_TYPE_FILE` - else - # exit won't necessarily exit 'configure', so signal an error this way, too: - echo INVALID - changequote([,]) - AC_MSG_ERROR(["$PKG_TYPE_FILE" is missing.]) - changequote(<,>) - fi - - # Check consistency of 'DIR/type' file - if [ "$PKG_TYPE" != "base" ] && \ - [ "$PKG_TYPE" != "standard" ] && \ - [ "$PKG_TYPE" != "optional" ] && \ - [ "$PKG_TYPE" != "experimental" ] && \ - [ "$PKG_TYPE" != "script" ] && \ - [ "$PKG_TYPE" != "pip" ]; then - # exit won't necessarily exit 'configure', so signal an error this way, too: - echo INVALID - changequote([,]) - AC_MSG_ERROR([The content of "$PKG_TYPE_FILE" must be 'base', 'standard', 'optional', 'experimental', 'script', or 'pip']) - changequote(<,>) - fi - - PKG_NAME=$(basename $DIR) - - # Filter - filter=no - if [ "$1" = all ]; then - filter=yes - elif [ "$1" = "$PKG_TYPE" ]; then - filter=yes - elif [ "$1" = sdist ]; then - # sdist packages are all standard packages, together with - # mpir and python2. - if [ "$PKG_TYPE" = standard ]; then - filter=yes - elif [ "$PKG_NAME" = mpir ]; then - filter=yes - elif [ "$PKG_NAME" = python2 ]; then - filter=yes - fi - fi - if [ $filter = yes ]; then - PKG_VAR="$(echo $PKG_NAME | sed 's/^/inst_/')" - PKG_VERSION=$(newest_version $PKG_NAME) - echo "$PKG_NAME $PKG_VERSION $PKG_VAR" - fi - done -} -changequote([,]) - -echo >&7 -echo >&7 '# Files to track installation of packages' -filtered_packages_list all | while read PKG_NAME PKG_VERSION PKG_VAR; do - if test "$PKG_NAME" = INVALID; then - # filtered_packages_list signals an error (already reported there) - exit 1 # this doesn't leave 'configure' (yet) - elif test "$PKG_NAME" != "$PKG_VERSION"; then - # If $need_to_install_{PKG_NAME} is set to no, then set $PKG_VAR - # to some dummy file to skip the installation. Note that an - # explicit "./sage -i PKG_NAME" will still install the package. - need_to_install="need_to_install_${PKG_NAME}" - if test "${!need_to_install}" != no ; then - echo >&7 "$PKG_VAR = \$(INST)/$PKG_VERSION" - AC_MSG_RESULT([ $PKG_VERSION]) - else - echo >&7 "$PKG_VAR = \$(INST)/.dummy" - AC_MSG_RESULT([ $PKG_VERSION not installed (configure check)]) - fi - fi -done || exit 1 - -echo >&7 -echo >&7 - -[ -echo >&7 '# All standard packages' -echo >&7 'STANDARD_PACKAGES = \' -filtered_packages_list standard | while read PKG_NAME PKG_VERSION PKG_VAR; do - if test "$PKG_NAME" = INVALID; then - # filtered_packages_list signals an error (already reported there) - exit 1 # this doesn't leave 'configure' (yet) - fi - echo >&7 " \$($PKG_VAR) \\" -done || exit 1 -echo >&7 -echo >&7 - -echo >&7 '# All optional installed packages (triggers the auto-update)' -echo >&7 'OPTIONAL_INSTALLED_PACKAGES = \' -filtered_packages_list optional | while read PKG_NAME PKG_VERSION PKG_VAR; do - if test "$PKG_NAME" = INVALID; then - # filtered_packages_list signals an error (already reported there) - exit 1 # this doesn't leave 'configure' (yet) - fi - if [ -f $SAGE_SPKG_INST/$PKG_NAME-* ]; then - echo >&7 " \$($PKG_VAR) \\" - fi; -done || exit 1 -echo >&7 -echo >&7 - -echo >&7 '# All packages which should be downloaded' -echo >&7 'SDIST_PACKAGES = \' -filtered_packages_list sdist | while read PKG_NAME PKG_VERSION PKG_VAR; do - if test "$PKG_NAME" = INVALID; then - # filtered_packages_list signals an error (already reported there) - exit 1 # this doesn't leave 'configure' (yet) - fi - echo >&7 " \$(INST)/$PKG_VERSION \\" -done || exit 1 -echo >&7 -echo >&7 +AC_SUBST([SAGE_TOOLCHAIN]) +SAGE_SPKG_COLLECT() # We need sage-env-config to exist before running this. # TODO: fix this in Trac #21524 touch "$SAGE_SRC/bin/sage-env-config" -echo >&7 'SCRIPTS = \' +SAGE_SCRIPTS='\ +' for file in "$SAGE_SRC/bin/"*; do # Skip files with ".in" extension ext=${file##*.} if test "$ext" != in; then - echo >&7 " \$(SAGE_LOCAL)${file#$SAGE_SRC} \\" + SAGE_SCRIPTS+=" \$(SAGE_LOCAL)${file#$SAGE_SRC} \\"$'\n' fi done -echo >&7 -echo >&7 'EXTCODE = \' -for file in `find "$SAGE_SRC"/ext -type f`; do - echo >&7 " \$(SAGE_EXTCODE)${file#$SAGE_SRC/ext} \\" -done -echo >&7 - -# Copy build/make/deps -cat >&7 <&7 -# Copy build/make/deps -cat >&7 </dependencies files -#============================================================================== - -EOF - - -# To deal with ABI incompatibilities when gcc is upgraded, every package -# (except gcc) should depend on gcc if gcc is already installed. -# See https://trac.sagemath.org/ticket/24703 -if test x$SAGE_INSTALL_GCC = xexists; then - GCC_DEP=' $(SAGE_LOCAL)/bin/gcc' -else - GCC_DEP='' -fi - -# Add a Makefile target corresponding to a given package -filtered_packages_list all | { -while read PKG_NAME PKG_VERSION PKG_VAR; do - if test "$PKG_NAME" = INVALID; then - # filtered_packages_list signals an error (already reported there) - exit 1 # this doesn't leave 'configure' (yet) - fi - DEP_FILE="$SAGE_ROOT/build/pkgs/$PKG_NAME/dependencies" - TYPE=`cat "$SAGE_ROOT/build/pkgs/$PKG_NAME/type"` - if [ -f "$DEP_FILE" ]; then - # DEPS is first line of $DEP_FILE with some modifications: - # - start with a single space - # - the # symbol is treated as comment which is removed - # - lower-case words "foo" are replaced by "$(inst_foo)" - DEPS=`sed 's/^ */ /; s/ *#.*//; s/ \([a-z0-9][a-z0-9_]*\)/ $(inst_\1)/g; q' $DEP_FILE` - elif [ "$TYPE" = optional ]; then - DEPS=' | $(STANDARD_PACKAGES)' # default for optional packages - elif [ "$TYPE" = script ]; then - DEPS=' | $(STANDARD_PACKAGES)' # default for script-only packages - elif [ "$TYPE" = pip ]; then - DEPS=' | $(inst_pip)' - else - DEPS="" - fi - test "$PKG_NAME" = gcc || DEPS="$GCC_DEP$DEPS" - - # Below we have to obscure A""M_V_at, or autoconf would complain: - # "error: possibly undefined macro: A""M_V_at" - if [ "$TYPE" = pip ]; then - # Special rules using PIP - echo >&7 "$PKG_NAME:$DEPS" - echo >&7 " \$(A""M_V_at)sage-logger -p 'sage --pip install $PKG_NAME' \$(SAGE_LOGS)/$PKG_NAME.log" - echo >&7 - - # Add a target to remove the "installed" file for "sage -f" - echo >&7 "$PKG_NAME-clean:" - echo >&7 " -sage --pip uninstall -y $PKG_NAME" - echo >&7 - elif [ "$TYPE" = script ]; then - # Just run the spkg-install script - echo >&7 "$PKG_NAME:$DEPS" - echo >&7 " \$(A""M_V_at)cd '$SAGE_ROOT' && \\" - echo >&7 " source '$SAGE_ROOT/src/bin/sage-env' && \\" - echo >&7 " sage-logger -p '$SAGE_ROOT/build/pkgs/$PKG_NAME/spkg-install' \$(SAGE_LOGS)/$PKG_NAME.log" - echo >&7 - - # Just run the spkg-uninstall script if it exists - echo >&7 "$PKG_NAME-clean:" - echo >&7 " -\$(A""M_V_at)cd '$SAGE_ROOT' && \\" - echo >&7 " source '$SAGE_ROOT/src/bin/sage-env' && \\" - echo >&7 " '$SAGE_ROOT/build/pkgs/$PKG_NAME/spkg-uninstall'" - echo >&7 - else - # Normal Sage packages - echo >&7 "\$(INST)/$PKG_VERSION:$DEPS" - echo >&7 " +\$(A""M_V_at)sage-logger -p '\$(SAGE_SPKG) $PKG_VERSION' '\$(SAGE_LOGS)/$PKG_VERSION.log'" - echo >&7 - - # Add a target with just the bare package name for "sage -i" - echo >&7 "$PKG_NAME: \$(INST)/$PKG_VERSION" - echo >&7 - - # Add a target to remove the "installed" file for "sage -f" - echo >&7 "$PKG_NAME-clean:" - echo >&7 " rm -f \$(INST)/$PKG_VERSION" - echo >&7 - fi +SAGE_EXTCODE='\ +' +for file in `find "$SAGE_SRC"/ext -type f`; do + SAGE_EXTCODE+=" \$(SAGE_EXTCODE)${file#$SAGE_SRC/ext} \\"$'\n' +done - PHONY="$PHONY $PKG_NAME" -done || exit 1 -echo >&7 ".PHONY:$PHONY" -} +AC_SUBST([SAGE_EXTCODE]) -# Close the Makefile -exec 7>&- -] +SAGE_MAKE_DEPS="$SAGE_ROOT/build/make/deps" +AC_SUBST_FILE([SAGE_MAKE_DEPS]) dnl AC_CONFIG_HEADERS([config.h]) -AC_CONFIG_FILES([build/make/Makefile-auto src/Makefile]) +AC_CONFIG_FILES([build/make/Makefile-auto build/make/Makefile src/Makefile]) AC_CONFIG_FILES([src/bin/sage-env-config]) -AC_CONFIG_MACRO_DIR([m4]) dnl Create basic directories needed for Sage AC_CONFIG_COMMANDS(mkdirs, diff --git a/m4/sage_spkg_collect.m4 b/m4/sage_spkg_collect.m4 new file mode 100644 index 00000000000..053e71e43fc --- /dev/null +++ b/m4/sage_spkg_collect.m4 @@ -0,0 +1,224 @@ +# SYNOPSIS +# +# SAGE_SPKG_COLLECT +# +# DESCRIPTION +# +# This macro gathers up information about SPKGs defined in the build/pkgs +# directory of the Sage source tree, and generates variables to be +# substitued into the build/make/Makefile.in template which list all the +# SPKGs, their versions, their dependencies, and categorizes them based +# on how they should be installed. +# +# In particular, this generates the Makefile variables: +# +# - SAGE_BUILT_PACKAGES - lists the names of SPKGs that should be built +# and installed from source. +# +# - SAGE_DUMMY_PACKAGES - lists the names of packages that are not built +# as part of Sage--either they are not required for the current +# platform, or the dependency on them is satisfied by an existing +# system package. +# +# - SAGE_STANDARD_PACKAGES - lists the names of all packages that have +# the "standard" type. All "standard" packages are installed by +# default (if they are listed in SAGE_DUMMY_PACKAGES "installed" in +# this case is a no-op). +# +# - SAGE_OPTIONAL_PACKAGES - lists the names of packages with the +# "optional" type that should be installed. +# +# - SAGE_SDIST_PACKAGES - lists the names of all packages that should be +# included in the source distribution. +# +# - SAGE_PACKAGE_VERSIONS - this template variable defines multiple +# Makefile variables in the format "vers_" the value +# of which is the current version of the SPKG . +# +# - SAGE_PACKAGE_DEPENDENCIES - this template variable defines multiple +# Makefile variables in the format "deps_" the value +# of which is the names of the dependencies of as read +# from the build//dependencies file. +# +# - SAGE_NORMAL_PACKAGES - lists the names of packages that are installed +# by the "normal" method (using the sage-spkg program to download and +# extract the source tarball, and run the relevant scripts from +# build//spkg-*. +# +# - SAGE_PIP_PACKAGES - lists the names of packages with the "pip" type +# which are installed by directly invoking the pip command. +# +# - SAGE_SCRIPT_PACKAGES - lists the names of packages with the "script" +# type which are installed by running a custom script, which may +# download additional source files. +# +AC_DEFUN_ONCE([SAGE_SPKG_COLLECT], [ +# To deal with ABI incompatibilities when gcc is upgraded, every package +# (except gcc) should depend on gcc if gcc is already installed. +# See https://trac.sagemath.org/ticket/24703 +if test x$SAGE_INSTALL_GCC = xexists; then + GCC_DEP=' $(SAGE_LOCAL)/bin/gcc' +else + GCC_DEP='' +fi + +AC_MSG_CHECKING([package versions]) +AC_MSG_RESULT([]) + +# Usage: newest_version $pkg +# Print version number of latest package $pkg +newest_version() { + PKG=$[1] + if test -f "$SAGE_ROOT/build/pkgs/$PKG/package-version.txt" ; then + cat "$SAGE_ROOT/build/pkgs/$PKG/package-version.txt" + else + echo "$PKG" + fi +} + +# Lists of packages that are actually built/installed and dummy packages +SAGE_BUILT_PACKAGES='\ +' +SAGE_DUMMY_PACKAGES='\ +' +# List of all standard packages +SAGE_STANDARD_PACKAGES='\ +' +# List of all currently installed optional packages +SAGE_OPTIONAL_INSTALLED_PACKAGES='\ +' +# List of all packages that should be downloaded +SAGE_SDIST_PACKAGES='\ +' +# Generate package version and dependency lists +SAGE_PACKAGE_VERSIONS="" +SAGE_PACKAGE_DEPENDENCIES="" +# Lists of packages categorized according to their build rules +SAGE_NORMAL_PACKAGES='\ +' +SAGE_PIP_PACKAGES='\ +' +SAGE_SCRIPT_PACKAGES='\ +' + +# for each package in pkgs/, add them to the SAGE_PACKAGE_VERSIONS and +# SAGE_PACKAGE_DEPENDENCIES lists, and to one or more of the above variables +# depending on the package type and other criteria (such as whether or not it +# needs to be installed) + +for DIR in $SAGE_ROOT/build/pkgs/*; do + test -d "$DIR" || continue + + PKG_TYPE_FILE="$DIR/type" + if test -f "$PKG_TYPE_FILE"; then + PKG_TYPE=`cat $PKG_TYPE_FILE` + else + AC_MSG_ERROR(["$PKG_TYPE_FILE" is missing.]) + fi + + PKG_NAME=$(basename $DIR) + PKG_VERSION=$(newest_version $PKG_NAME) + + in_sdist=no + + # Check consistency of 'DIR/type' file + case "$PKG_TYPE" in + base) ;; + standard) + SAGE_STANDARD_PACKAGES+=" $PKG_NAME \\"$'\n' + in_sdist=yes + ;; + optional) + if test -f $SAGE_SPKG_INST/$PKG_NAME-*; then + SAGE_OPTIONAL_INSTALLED_PACKAGES+=" $PKG_NAME \\"$'\n' + fi; + ;; + experimental) ;; + script) ;; + pip) ;; + *) + AC_MSG_ERROR([The content of "$PKG_TYPE_FILE" must be 'base', 'standard', 'optional', 'experimental', 'script', or 'pip']) + ;; + esac + + SAGE_PACKAGE_VERSIONS+="vers_$PKG_NAME = $PKG_VERSION"$'\n' + + # If $need_to_install_{PKG_NAME} is set to no, then set inst_ to + # some dummy file to skip the installation. Note that an explicit + # "./sage -i PKG_NAME" will still install the package. + if test "$PKG_NAME" != "$PKG_VERSION"; then + need_to_install="need_to_install_${PKG_NAME}" + + if test "${!need_to_install}" != no ; then + SAGE_BUILT_PACKAGES+=" $PKG_NAME \\"$'\n' + AC_MSG_RESULT([ $PKG_NAME-$PKG_VERSION]) + else + SAGE_DUMMY_PACKAGES+=" $PKG_NAME \\"$'\n' + AC_MSG_RESULT([ $PKG_NAME-$PKG_VERSION not installed (configure check)]) + fi + fi + + # Packages that should be included in the source distribution + # This includes all standard packages and two special cases + case "$PKG_NAME" in + mpir|python2) + in_sdist=yes + ;; + esac + + if test "$in_sdist" = yes; then + SAGE_SDIST_PACKAGES+=" $PKG_NAME \\"$'\n' + fi + + # Determine package dependencies + DEP_FILE="$SAGE_ROOT/build/pkgs/$PKG_NAME/dependencies" + if test -f "$DEP_FILE"; then + # - the # symbol is treated as comment which is removed + DEPS=`sed 's/^ *//; s/ *#.*//; q' $DEP_FILE` + else + case "$PKG_TYPE" in + optional) + DEPS=' | $(STANDARD_PACKAGES)' # default for optional packages + ;; + script) + DEPS=' | $(STANDARD_PACKAGES)' # default for script-only packages + ;; + pip) + DEPS=' | pip' + ;; + *) + DEPS="" + ;; + esac + fi + + # Special case for GCC; see definition of GCC_DEP above + test "$PKG_NAME" = gcc || DEPS="$GCC_DEP$DEPS" + + SAGE_PACKAGE_DEPENDENCIES+="deps_$PKG_NAME = $DEPS"$'\n' + + # Determine package build rules + case "$PKG_TYPE" in + pip) + SAGE_PIP_PACKAGES+=" $PKG_NAME \\"$'\n' + ;; + script) + SAGE_SCRIPT_PACKAGES+=" $PKG_NAME \\"$'\n' + ;; + *) + SAGE_NORMAL_PACKAGES+=" $PKG_NAME \\"$'\n' + ;; + esac +done + +AC_SUBST([SAGE_PACKAGE_VERSIONS]) +AC_SUBST([SAGE_PACKAGE_DEPENDENCIES]) +AC_SUBST([SAGE_NORMAL_PACKAGES]) +AC_SUBST([SAGE_PIP_PACKAGES]) +AC_SUBST([SAGE_SCRIPT_PACKAGES]) +AC_SUBST([SAGE_BUILT_PACKAGES]) +AC_SUBST([SAGE_DUMMY_PACKAGES]) +AC_SUBST([SAGE_STANDARD_PACKAGES]) +AC_SUBST([SAGE_OPTIONAL_INSTALLED_PACKAGES]) +AC_SUBST([SAGE_SDIST_PACKAGES]) +]) diff --git a/src/bin/sage b/src/bin/sage index 484528d16f5..f795182640d 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -355,7 +355,10 @@ if [ "$1" = '-i' ]; then for PKG in $PACKAGES; do echo # First check that $PKG is actually a Makefile target - if ! grep "^$PKG: " build/make/Makefile >/dev/null; then + # Since https://trac.sagemath.org/ticket/21524 there is not explicitly + # a target for the package, but if it has a vers_ variable + # there will also be a generated rule for it + if !grep "^vers_$PKG = " build/make/Makefile >/dev/null; then echo >&2 "Error: package '$PKG' not found" echo >&2 "Note: if it is an old-style package, use -p instead of -i to install it" exit 1