# Comfignat makefile foundation for configuring and building GNAT projects # Copyright 2013 B. Persson, Bjorn@Rombobeorn.se # # This material is provided as is, with absolutely no warranty expressed # or implied. Any use is at your own risk. # # Permission is hereby granted to use or copy this makefile # for any purpose, provided the above notices are retained on all copies. # Permission to modify the code and to distribute modified code is granted, # provided the above notices are retained, and a notice that the code was # modified is included with the above copyright notice. # This file contains generic Make code. It is designed to be included by other # makefiles, called containing makefiles, which add information specific to the # project at hand. Builds are controlled by GNAT project files which import the # abstract project Comfignat and use the directory variables it defines. For # libraries there shall also be usage projects to be installed on the target # system. Usage projects and the Comfignat project will be preprocessed with # Gnatprep. (Build projects may also be preprocessed.) # # If a directories project is provided, then the project files will get the # directory variables from there, otherwise the Make variables will be used. # # This file may not work with other Make clones than GNU Make. (Reusable Make # code is pretty much impossible to write without advanced Make features.) If # Make cannot be used for whatever reason, then it's not too difficult to run # the project files through Gnatprep manually. # # Program-name variables and the usual options variables are picked up from the # environment or the command line: # GNATPREP ?= gnatprep GNAT_BUILDER ?= gprbuild # If GNAT_BUILDER looks like it will invoke Gnatmake, then make the default # value of GNATFLAGS compatible with Gnatmake. Otherwise make it suitable for # building multi-language projects with GPRbuild. GNATFLAGS ?= ${if ${findstring gnatmake, \ ${notdir ${call mung,${GNAT_BUILDER}}}}, \ ${GNAT_BUILDER_FLAGS} \ -cargs ${ADAFLAGS} \ -bargs ${GNATBINDFLAGS} \ -largs ${GNATLINKFLAGS} ${LDFLAGS}, \ ${GNAT_BUILDER_FLAGS} \ -cargs:Ada ${ADAFLAGS} \ -cargs:C ${CPPFLAGS} ${CFLAGS} \ -cargs:C++ ${CPPFLAGS} ${CXXFLAGS} \ -cargs:Fortran ${FFLAGS} \ -bargs ${GNATBINDFLAGS} \ -largs ${LDFLAGS}} # (DESTDIR is also supported.) # Containing makefiles may assign default values to the options variables # GNAT_BUILDER_FLAGS, ADAFLAGS, CPPFLAGS, CFLAGS, CXXFLAGS, FFLAGS, # GNATBINDFLAGS, GNATLINKFLAGS and LDFLAGS if they are undefined in the # environment, but should expect that users and distributions may override # those defaults. # # These variables should be overridden on the command line as needed, but will # not be picked up from the environment: # dirgpr = # dirgpr should be the filename of the target system's directories project if # there is one. The Gnatprep symbols Directories_GPR and Directories_Project # will be derived from dirgpr, and project files will be configured to use the # directories project. relocatable_package = false # If relocatable_package is true, then directory variables in project files # will be configured with relative pathnames so that the installed directory # tree as a whole can be moved to another location in the filesystem without # breaking the project files. # dirgpr takes precedence over relocatable_package. prefix = /usr/local exec_prefix = ${prefix} bindir = ${exec_prefix}/bin libexecdir = ${exec_prefix}/libexec datarootdir = ${prefix}/share datadir = ${datarootdir} sysconfdir = ${prefix}/etc localstatedir = ${prefix}/var statedir = ${localstatedir}/lib cachedir = ${localstatedir}/cache logdir = ${localstatedir}/log runtimedir = /run lockdir = ${runtimedir}/lock includedir = ${prefix}/include libdir = ${exec_prefix}/lib gprdir = ${datarootdir}/gpr localedir = ${datarootdir}/locale mandir = ${datarootdir}/man infodir = ${datarootdir}/info miscdocdir = ${datarootdir}/doc # These are the directories where different kinds of files will be installed on # the target system. Some of these directory variables aren't used in this file # but may be needed in containing makefiles. builddir = ${CURDIR}/build objdir = ${builddir}/obj stagedir = ${builddir}/stage # Intermediate files produced during the build are kept in objdir. Files to be # installed are written to stagedir, and then copied to their destination in # the installation step. # Containing makefiles should avoid modifying the directory variables. Users # should be able to rely on these defaults. install_cp_flags = ${if ${DESTDIR},--preserve=timestamps,} # Timestamps are preserved when installation is done to a staging directory. # This matters for files that aren't generated during the build but copied from # the source tree. Timestamps are not preserved when installation is done # directly to the target system, because that would change the timestamps of # existing directories. # # Containing makefiles should assign or append to these variables as needed: # ifneq (${origin preprocessed_files},file) preprocessed_files := ${basename ${wildcard *.in}} endif # preprocessed_files is a list of files to be produced in the preprocessing # step at the beginning of the build. Containing makefiles may override it or # append additional filenames to it. ifneq (${origin build_GPRs},file) build_GPRs := endif # build_GPRs shall name one or more project files for building the software. # These project files will be used when "make" or "make build" is invoked. ifneq (${origin usage_GPRs},file) usage_GPRs := endif # If the build produces libraries, then usage_GPRs shall name the project files # that other projects should import to link to the libraries. These project # files will be installed to the target system. ifneq (${origin options},file) options := endif # options may be assigned a list of variable names. Those variables may be # overridden on the command line, and will be defined as Gnatprep symbols. # Their values must be "true" or "false". # The containing makefile should assign a default value to each variable unless # it shall be mandatory to always set the option on the command line. ifneq (${origin Gnatprep_definitions},file) Gnatprep_definitions := endif # Any text assigned to Gnatprep_definitions will be included in the Gnatprep # command line. It may be used for additional symbol definitions. configuration_variables += \ GNATPREP GNAT_BUILDER \ GNAT_BUILDER_FLAGS ADAFLAGS CPPFLAGS CFLAGS CXXFLAGS FFLAGS GNATBINDFLAGS \ GNATLINKFLAGS LDFLAGS GNATFLAGS \ DESTDIR \ dirgpr relocatable_package \ prefix exec_prefix bindir libexecdir datarootdir datadir sysconfdir \ localstatedir statedir cachedir logdir runtimedir lockdir includedir libdir \ gprdir localedir mandir infodir miscdocdir \ builddir objdir stagedir \ install_cp_flags \ ${options} # configuration_variables is a list of variables that can be saved in the # persistent configuration with "make configure". Containing makefiles may # append additional variable names. # # Containing makefiles may use this command variable: # build_GPR = "${GNAT_BUILDER}" -P ${firstword ${filter %.gpr,$^}} -p \ ${GNATFLAGS} -margs # build_GPR is a command for use in recipes. It performs a build controlled by # the first project file among the rule's prerequisites. # Containing makefiles may append additional arguments for the builder, but # should ensure that any arguments that aren't essential for the build to work # can be overridden from the command line. Global default values for optional # arguments should be set in the options variables instead. # # Read the configuration file if there is one: # -include comfignat_configuration.mk # # Compute the symbol definitions for Gnatprep, and some other data that the # rules need: # nil = space_sub = _Comfignat_magic_protective_space_character_substitute_ percent_sub = _Comfignat_magic_protective_percent_character_substitute_ mung = ${subst %,${percent_sub},${subst ${nil} ,${space_sub},${1}}} unmung = ${subst ${percent_sub},%,${subst ${space_sub}, ,${1}}} # mung and unmung are used to prevent Make from interpreting space and percent # characters in strings. # Convey objdir and stagedir to Gnatprep. directories := '-DObjdir="${objdir}"' '-DStagedir="${stagedir}"' # directories is simply expanded as this reduces redundant processing and the # directory variables are not to be modified after this point. ifneq (${dirgpr},) # A directories project is used, so make project files take the directory # variables from there. directories_project := ${basename ${notdir ${dirgpr}}} directories += '-DDirectories_GPR="${dirgpr}"' directories += '-DDirectories_Project=${directories_project}' directories += '-DPrefix="${prefix}"' directories += '-DExec_Prefix="${exec_prefix}"' directories += '-DBindir=${directories_project}.Bindir' directories += '-DLibexecdir=${directories_project}.Libexecdir' directories += '-DIncludedir=${directories_project}.Includedir' directories += '-DLibdir=${directories_project}.Libdir' else ifeq (${relocatable_package},true) # Make project files use directory names relative to gprdir. relativize = ${if ${filter ${2}%,${1}}, \ ${3}${1:${2}%=%}, \ ${call relativize,${1},${dir ${2:%/=%}},${3}../}} # relativize is the recursive algorithm that converts an absolute pathname # into a relative one. # Parameters: # 1: an absolute pathname to convert to relative # 2: the absolute base pathname, being shortened until it's a prefix of 1 # 3: a growing series of "../" to lead the relative pathname with # If 2 is a prefix of 1, then return 3 concatenated with the part of 1 that # differs from 2. Otherwise delete the last element of 2, add one level of # "../" to 3, and repeat. # Within relativize all pathnames have one trailing slash so that only whole # directory names will match. Otherwise "/usr/lib" could match "/usr/lib64" # for example. prep = ${subst //,/,${abspath ${call mung,${1}}}/} # prep prepares a pathname for use as a parameter to relativize. # · Protect space and percent characters from interpretation by Make. # · Normalize the pathname, eliminating ".", ".." and "//". # · Append a slash. # · If the input was "/", then it is now "//". Change that back to "/". relative_to_gprdir = \ ${or ${call unmung,${patsubst %/,%,${call relativize \ ,${call prep,${1}} \ ,${call prep,${gprdir}},}}},.} # relative_to_gprdir converts an absolute pathname into a pathname relative # to gprdir. What it actually does is to prepare the input to relativize and # fix up its output. # · Prepare the input pathname with prep. # · Prepare gprdir with prep. # · Call relativize with the input for parameter 1, gprdir for 2, and an # empty string for 3. # · Strip the result of surrounding spaces and the trailing slash. # · Reverse the protection of space and percent characters. # · If the result is an empty string, then return "." instead. directories += '-DBase="${gprdir}"' directories += '-DPrefix="${call relative_to_gprdir,${prefix}}"' directories += '-DExec_Prefix="${call relative_to_gprdir,${exec_prefix}}"' directories += '-DBindir="${call relative_to_gprdir,${bindir}}"' directories += '-DLibexecdir="${call relative_to_gprdir,${libexecdir}}"' directories += '-DIncludedir="${call relative_to_gprdir,${includedir}}"' directories += '-DLibdir="${call relative_to_gprdir,${libdir}}"' else ifeq (${relocatable_package},false) # Convey the directory variables unmodified to project files. directories += '-DPrefix="${prefix}"' directories += '-DExec_Prefix="${exec_prefix}"' directories += '-DBindir="${bindir}"' directories += '-DLibexecdir="${libexecdir}"' directories += '-DIncludedir="${includedir}"' directories += '-DLibdir="${libdir}"' else ${error relocatable_package must be "true" or "false".} endif definitions = ${directories} # definitions is recursively expanded so that the options referenced below may # be defined in containing makefiles after the inclusion of this file. # Convey boolean options to Gnatprep. definitions += \ ${foreach option,${options}, \ ${if ${and ${filter-out environment,${origin ${option}}}, \ ${filter 1,${words ${${option}}}}, \ ${filter true false,${${option}}}}, \ -D${option}=${${option}}, \ ${error ${option} must be "true" or "false".}}} # For each variable listed in options, check that it didn't come from the # environment (to prevent accidents), that its value is a single word, and that # that word is either "true" or "false". If so, append a symbol definition; # otherwise complain and stop. # Convey any additional symbols that the containing makefile has defined. definitions += ${Gnatprep_definitions} build_targets = ${addsuffix .phony_target,${build_GPRs}} # A phony target is defined for each build project, and the job of determining # whether the project needs rebuilding is delegated to the builder. stage_any_GPRs = ${if ${usage_GPRs},stage_GPRs} # The recipe for the target stage_GPRs breaks if usage_GPRs is empty, so it's # included as a prerequisite of build only if there are some usage projects. # # Make rules: # .SECONDEXPANSION: all: build configure:: @( ${foreach variable,${configuration_variables}, \ ${if ${or ${findstring command line, \ ${origin ${variable}}}, \ ${filter true,${${variable}_is_configured}}}, \ echo '${variable} = ${value ${variable}}'; \ echo '${variable}_is_configured = true';}} \ ) > comfignat_configuration.mk # Out of the variables listed in configuration_variables, all that were # overridden on the command line, and all that were previously configured, are # written to the configuration file. A variable is considered previously # configured if there is another variable with "_is_configured" appended to its # name and a value of "true". Such a variable is also written for each # configured variable. It is therefore possible to delete a variable V from the # configuration by running "make configure V_is_configured=false". %.gpr: %.gpr.in "${GNATPREP}" $< $@ ${definitions} preprocess: $${preprocessed_files} %.gpr.phony_target: %.gpr preprocess ${build_GPR} # Instead of tracking dependencies between project files, this rule simply # requires that all preprocessing is done before any project is built. stage_GPRs: $${usage_GPRs} mkdir -p "${stagedir}${gprdir}" cp -p ${usage_GPRs} "${stagedir}${gprdir}" .PHONY: stage_GPRs build: $${build_targets} $${stage_any_GPRs} ${stagedir}: @${MAKE} build --no-print-directory # "make install" straight out of a source package triggers a build, but if # something has been built then "make install" doesn't rebuild anything, just # copies the built files to their destination. install: ${stagedir} mkdir -p "${DESTDIR}/" cp -RPf ${install_cp_flags} "${stagedir}"/* "${DESTDIR}/" .PHONY: install clean:: rm -Rf ${builddir} ${preprocessed_files} unconfigure:: rm -f comfignat_configuration.mk distclean: clean unconfigure