From bc173807f08e4f8b3562416046d4eabea7df2b54 Mon Sep 17 00:00:00 2001 From: Justin LaPolla Date: Tue, 6 Feb 2018 09:28:47 -0600 Subject: [PATCH] Expand 'cray' toolset --- src/tools/cray.jam | 1162 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 1053 insertions(+), 109 deletions(-) diff --git a/src/tools/cray.jam b/src/tools/cray.jam index a64f1080a..29f843cba 100644 --- a/src/tools/cray.jam +++ b/src/tools/cray.jam @@ -1,125 +1,1069 @@ -# Copyright 2001 David Abrahams. -# Copyright 2004, 2005 Markus Schoepflin. -# Copyright 2011, John Maddock -# Copyright 2013, Cray, Inc. +# Copyright 2001 David Abrahams +# Copyright 2004, 2005 Markus Schoepflin +# Copyright 2011 John Maddock +# Copyright 2013, 2017-2018 Cray, Inc. +# # Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# README.md # -# Cray C++ Compiler -# See http://docs.cray.com/books/S-2179-50/html-S-2179-50/S-2179-50-toc.html +# This toolset is for the Cray Compiling Environment (CCE). # +# The assembler, linker, and archiver are the same as those used in the +# `gcc` toolset. Therefore, there is some duplication of code between the +# `gcc` toolset and this toolset. +# +# # CCE Introduction +# +# Users want to compile and run massively parallel applications on Cray +# supercomputers. Typically, the user compiles code on a login node of the +# supercomputer and then runs the compiled program on multiple compute +# nodes using a batch control system. This means the user is almost always +# cross compiling. +# +# But, they're not just cross compiling. In order for a program to run on +# a Cray supercomputer it has to link to particular libraries. There are +# three general categories of libraries that user programs must link to: +# +# - Network libraries: Enable communication between processes on different +# compute nodes. Depends on the network hardware in the supercomputer. +# - Compute node libraries: Depends on the hardware on the targetted +# compute nodes. +# - Language extension libraries: Depends on the language extensions used +# by the program (e.g. OpenMP, Unified Parallel C, et cetera). +# +# Instead of forcing users to invoke the compiler with a bunch of +# libraries listed on the command line, CCE decides what libraries to link +# based on the environment. This is primarily controlled by loading and +# unloading modules (with the `module` command) to create a cross +# compiling and linking environment suitable for the particular hardware +# on the targetted Cray supercomputer. +# +# CCE compilers come in two parts: the compiler itself, and the compiler +# driver. Invoking a compiler directly is not supported. We must always +# invoke the compiler through a compiler driver: either `cc` for C code, +# `CC` for C++ code, or `ftn` for Fortran code. The compiler driver is +# responsible for gathering information from the environment and invoking +# the selected compiler with the appropriate command line options. +# +# For more information on CCE, search for Cray publication S-2529 on the +# Cray publications website (https://pubs.cray.com). -import feature generators common ; -import toolset : flags ; +import "class" : new ; +import common ; +import feature ; +import gcc ; +import generators ; +import os ; +import regex ; +import set ; +import toolset ; +import type ; +import unix ; + +### +### 'init' +### + +rule init ( : : options * : requirements * ) +{ + + check-prgenv-module ; + + # User cannot specify a 'version' in their 'using' statement. Compiler + # version is always controlled by loading and unloading modules in the + # user's environment. + + # User cannot specify a 'command' in their 'using' statement. Using a + # single 'command' argument only makes sense when a single executable can + # compile different types of code (e.g. gcc will compile C or C++ based on + # the file name extensions). In CCE, you have to invoke one of the three + # compiler drivers: cc for C code, CC for C++ code, or ftn for Fortran + # code. Each compiler driver compiles a single type of source code. It is + # possible to let the user pass in three 'command' arguments, one for each + # driver, but that seems like more effort that it's worth. + + local toolset = cray ; + + local command-c = [ validate-command $(toolset) cc ] ; + local command-c++ = [ validate-command $(toolset) CC ] ; + local command-fortran = [ validate-command $(toolset) ftn ] ; + + # Archive builder. + local command-ar = [ validate-command $(toolset) ar ] ; + + # Archive indexer. + local command-ranlib = [ validate-command $(toolset) ranlib ] ; + + # The 'command' variables always have one element, but they may contain + # spaces (e.g. if 'command' is an absolute path and some path components + # have spaces). + + # FIXME: Detect compiler version instead of hard coding a version. + local dotted-version = 8.6.5 ; + local version = [ numeric-version $(dotted-version) ] ; + + { + + # 'check-init-parameters' ensures that each time a toolset is initialized, + # it is initialized with a unique configuration. The return value is a + # Boost.Build property condition which uniquely indentifies this + # configured instance of this toolset. Typically, toolsets use the + # returned condition as the conditional in a 'toolset.flags' call to set + # flags specific to this configuration of this toolset. + + local identifying-condition = [ common.check-init-parameters $(toolset) $(requirements) ] ; + + # 'handle-options' uses 'toolset.flags' to set 'CONFIG_COMMAND' variables + # on targets when this toolset is used. The 'CONFIG_COMMAND' variables + # specify the commands to call for compiling. This would be more relevant + # if our 'init' rule had arguments that might affect the command that is + # invoked (e.g. in many toolsets 'version' affects the name of the + # compiler command). For now, we'll do this because it is a common pattern + # in toolsets, and we may need it in the future. + + handle-options + $(toolset) + : $(identifying-condition) + : $(command-c) $(command-c++) $(command-fortran) $(command-ar) $(command-ranlib) + : $(options) ; + + # Add compiler version to 'VERSION' variable on all targets. 'VERSION' is + # not used in any actions, but it is used in some updating rule + # procedures. + + toolset.flags $(toolset) VERSION $(identifying-condition) : $(version) ; + } +} + +rule check-prgenv-module ( ) +{ + + local compiler = [ os.environ PE_ENV ] ; + compiler = $(compiler:L) ; + + # We could check that environment variable CRAY_PRGENV$PE_ENV is set to + # "loaded", but this seems unnecessary and redundant. + + local default-compiler = cray ; + + if ! $(compiler) + { + log-warning $(toolset) : no PrgEnv module loaded + : falling back to PrgEnv-$(default-compiler) + : please load the PrgEnv-$(default-compiler) module next time ; + compiler = $(default-compiler) ; + } + + if $(compiler) != $(default-compiler) + { + log-error $(toolset) : compiler '$(compiler)' not supported + : toolset initialization failed + : please load the PrgEnv-$(default-compiler) module next time ; + # Do not abort, as suggested by: + # http://www.boost.org/build/doc/html/bbv2/extending/toolset_modules.html. + } +} + +### +### Command line options +### + +if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] +{ + + # Check if '--debug-configuration' was passed on the command line. This is + # inspired by 'common.jam' and other modules. + + # I think variable names that start with '.' signify variables that are + # considered properties of the module, though I'm not entirely sure. If it + # were a private variable, it would be declared 'local'. + + # The Jam language uses dynamic scoping. Setting '.debug-configuration' in + # this module influences the behavior of methods called from this module. + + .debug-configuration = true ; +} + +if [ MATCH (--debug-driver) : [ modules.peek : ARGV ] ] +{ + + .debug-driver = true ; +} + +### +### Features +### feature.extend toolset : cray ; -# Inherit from Unix toolset to get library ordering magic. -toolset.inherit cray : unix ; - -generators.override cray.prebuilt : builtin.lib-generator ; -generators.override cray.prebuilt : builtin.prebuilt ; -generators.override cray.searched-lib-generator : searched-lib-generator ; - - -rule init ( version ? : command * : options * ) -{ - local condition = [ common.check-init-parameters cray : version $(version) ] ; - - local command = [ common.get-invocation-command cray : CC : $(command) ] ; - - if $(command) - { - local root = [ common.get-absolute-tool-path $(command[-1]) ] ; - - if $(root) - { - flags cray .root $(condition) : "\"$(root)\"/" ; - } - } - # If we can't find 'CC' anyway, at least show 'CC' in the commands - command ?= CC ; - - common.handle-options cray : $(condition) : $(command) : $(options) ; -} - -generators.register-c-compiler cray.compile.c++ : CPP : OBJ : cray ; -generators.register-c-compiler cray.compile.c : C : OBJ : cray ; - - -# unlike most compliers, Cray defaults to static linking. -# flags cxx LINKFLAGS static : -bstatic ; -# flags cray.compile OPTIONS on : -G0 ; -flags cray.compile OPTIONS on : -g ; -# flags cray.link OPTIONS on : -G0 ; -flags cray.link OPTIONS on : -g ; - -flags cray.compile OPTIONS off : -O0 ; -flags cray.compile OPTIONS speed : -O2 ; -flags cray.compile OPTIONS space : -O1 ; - -# flags cray.compile OPTIONS off : -hipa0 ; -# flags cray.compile OPTIONS on : ; -# flags cray.compile OPTIONS full : -hipa5 ; - -flags cray.compile OPTIONS ; -flags cray.compile.c++ OPTIONS ; -flags cray.compile DEFINES ; -flags cray.compile INCLUDES ; -flags cray.link OPTIONS ; - -flags cray.compile OPTIONS : -hgnu -fPIC -h system_alloc -h tolerant -h ipa0 ; -flags cray.compile OPTIONS shared : -dynamic ; -flags cray.compile OPTIONS static : -static ; -flags cray.link OPTIONS static : -static ; -flags cray.link OPTIONS shared ; -flags cray.link LOPTIONS shared : -dynamic ; - -flags cray.link LIBPATH ; -flags cray.link LIBRARIES ; -flags cray.link FINDLIBS-ST ; -flags cray.link FINDLIBS-SA ; - -actions link bind LIBRARIES -{ - $(CONFIG_COMMAND) $(OPTIONS) $(LOPTIONS) -o "$(<)" -L$(LIBPATH) "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-ST) -l$(FINDLIBS-SA) -} - -# When creating dynamic libraries, we don't want to be warned about unresolved -# symbols, therefore all unresolved symbols are marked as expected by -# '-expect_unresolved *'. This also mirrors the behaviour of the GNU tool -# chain. - -actions link.dll bind LIBRARIES -{ - $(CONFIG_COMMAND) -o "$(<[1])" -Wl,-h -Wl,$(<[-1]:D=) -shared -L$(LIBPATH) "$(>)" "$(LIBRARIES)" -l$(FINDLIBS-ST) -l$(FINDLIBS-SA) $(OPTIONS) -} - - -# Note: Relaxed ANSI mode (-std) is used for compilation because in strict ANSI -# C89 mode (-std1) the compiler doesn't accept C++ comments in C files. As -std -# is the default, no special flag is needed. -actions compile.c -{ - $(.root:E=)cc -c $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -o "$(<)" "$(>)" -} - -# Note: The compiler is forced to compile the files as C++ (-x cxx) because -# otherwise it will silently ignore files with no file extension. +# Typically, extending '' with the value 'cray' would cause +# 'cray' to be the default '' as long as it is the first value +# added to ''. However, we already imported the 'gcc' toolset, so +# 'cray' is not the first value added to ''. Therefore, we need +# to call 'feature.set-default'. # -# Note: We deliberately don't suppress any warnings on the compiler command -# line, the user can always do this in a customized toolset later on. +# If the build request specified a '' (e.g. on the command line), +# then our default will not be used. However, if the 'cray' toolset was +# selected as the default in 'user-config.jam', then the build request +# will use the default ''. Therefore, we must use +# 'feature.set-default' so that default selecting the 'cray' toolset in +# 'user-config.jam' works correctly. + +feature.set-default toolset : cray ; + +# CCE is different from other compilers in that it optimizes, inlines, and +# vectorizes by default. Boost.Build assumes that 'off' is the default for +# all compilers. However, for CCE, 'off' and 'default' have different +# meanings. For CCE, 'off' requries an additional command line argument to +# turn the feature off. 'default' will not include an additional command +# line argument, but will do optimization, inlining, and vectorizing at +# whatever default level CCE uses. +# +# A user wishing to use CCE's default values may put the following in +# their 'user-config.jam' file: +# +# feature.set-default optimization : default ; +# feature.set-default inlining : default ; +# feature.set-default vectorize : default ; + +feature.extend optimization : default ; +feature.extend inlining : default ; +feature.extend vectorize : default ; + +### +### Flags +### + +# Updating rules are named in a dotted hierarcy. For example: +# +# compile +# \_ compile.c++ +# \_ compile.c++.preprocess +# \_ compile.c +# \_ compile.c.preprocess +# +# This naming convention allows us to apply flags to multiple children in +# the hierarchy. For example, if we apply a flag to 'compile.c++', that +# flag is also applied to its child 'compile.c++.preprocess'. If we apply +# a flag to 'compile', then that flag is applied to all children under +# 'compile'. + +toolset.flags cray.compile OPTIONS shared : -h pic ; + +toolset.flags cray.compile OPTIONS default ; # Blank. +toolset.flags cray.compile OPTIONS off : -O 0 ; +toolset.flags cray.compile OPTIONS speed : -O 3 ; +toolset.flags cray.compile OPTIONS space ; # Blank. CCE does not optimize for space. + +toolset.flags cray.compile OPTIONS default ; # Blank. +toolset.flags cray.compile OPTIONS off : -h ipa0 ; +toolset.flags cray.compile OPTIONS on ; # Blank. CCE does inlining by default. +toolset.flags cray.compile OPTIONS full : -h ipa5 ; + +toolset.flags cray.compile OPTIONS default ; # Blank. +toolset.flags cray.compile OPTIONS off : -h vector0 ; +toolset.flags cray.compile OPTIONS on ; # Blank. CCE vectorizes by default. +toolset.flags cray.compile OPTIONS full : -h vector3 ; + +toolset.flags cray.link FINDLIBS-SA multi : rt ; # Not sure if this is correct. + +toolset.flags cray.link OPTIONS shared : -h pic ; + +{ + # + # Link flags copied from 'gcc.jam'. + # + + local toolset = cray ; + local generic-os = [ set.difference [ feature.values ] : aix darwin vxworks solaris osf hpux ] ; + # Strip the binary when no debugging is needed. We use --strip-all flag + # as opposed to -s since icc (intel's compiler) is generally + # option-compatible with and inherits from the gcc toolset, but does not + # support -s. + toolset.flags $(toolset).link OPTIONS $(generic-os)/on : -Wl,--strip-all ; + toolset.flags $(toolset).link RPATH $(generic-os) : ; + toolset.flags $(toolset).link RPATH_OPTION $(generic-os) : -rpath ; + toolset.flags $(toolset).link RPATH_LINK $(generic-os) : ; + toolset.flags $(toolset).link START-GROUP $(generic-os) : -Wl,--start-group ; + toolset.flags $(toolset).link END-GROUP $(generic-os) : -Wl,--end-group ; + + # gnu ld has the ability to change the search behaviour for libraries + # referenced by the -l switch. These modifiers are -Bstatic and + # -Bdynamic and change search for -l switches that follow them. The + # following list shows the tried variants. Search stops at the first + # variant that has a match. + # + # *nix: -Bstatic -lxxx + # libxxx.a + # + # *nix: -Bdynamic -lxxx + # libxxx.so + # libxxx.a + # + # windows (mingw, cygwin) -Bstatic -lxxx + # libxxx.a + # xxx.lib + # + # windows (mingw, cygwin) -Bdynamic -lxxx + # libxxx.dll.a + # xxx.dll.a + # libxxx.a + # xxx.lib + # cygxxx.dll (*) + # libxxx.dll + # xxx.dll + # libxxx.a + # + # (*) This is for cygwin + # Please note that -Bstatic and -Bdynamic are not a guarantee that a + # static or dynamic lib indeed gets linked in. The switches only change + # search patterns! + + # On *nix mixing shared libs with static runtime is not a good idea. + toolset.flags $(toolset).link FINDLIBS-ST-PFX $(generic-os)/shared : -Wl,-Bstatic ; + toolset.flags $(toolset).link FINDLIBS-SA-PFX $(generic-os)/shared : -Wl,-Bdynamic ; + + toolset.flags $(toolset).link HAVE_SONAME $(generic-os) : "" ; + toolset.flags $(toolset).link SONAME_OPTION $(generic-os) : -h ; + + # See note [1] + toolset.flags $(toolset).link OPTIONS $(generic-os)/static : -static ; + + # [1] + # For static we made sure there are no dynamic libraries in the + # link. On HP-UX not all system libraries exist as archived libraries (for + # example, there is no libunwind.a), so, on this platform, the -static option + # cannot be specified. +} + +# Flags for 'free' features ('free' features are features that do not have +# a pre-defined set of values). + +toolset.flags cray.compile USER_OPTIONS ; +toolset.flags cray.compile.c++ USER_OPTIONS ; +toolset.flags cray.compile.asm USER_OPTIONS ; +toolset.flags cray.compile DEFINES ; +toolset.flags cray.compile INCLUDES ; + +toolset.flags cray.link USER_OPTIONS ; +toolset.flags cray.link LINKPATH ; +toolset.flags cray.link FINDLIBS-ST ; +toolset.flags cray.link FINDLIBS-SA ; +toolset.flags cray.link LIBRARIES ; + +toolset.flags cray.archive AROPTIONS ; + +### +### Actions +### actions compile.c++ { - $(CONFIG_COMMAND) -c $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -o "$(<)" "$(>)" + "$(CONFIG_COMMAND_CXX)" $(OPTIONS) $(USER_OPTIONS) -D$(SPACE)$(DEFINES) -I$(SPACE)"$(INCLUDES)" -c -o "$(<)" "$(>)" $(DRIVER_OPTIONS) } -# Always create archive from scratch. See the gcc toolet for rationale. -RM = [ common.rm-command ] ; -actions together piecemeal archive +actions compile.c { - $(RM) "$(<)" - ar rc $(<) $(>) + "$(CONFIG_COMMAND_C)" $(OPTIONS) $(USER_OPTIONS) -D$(SPACE)$(DEFINES) -I$(SPACE)"$(INCLUDES)" -c -o "$(<)" "$(>)" $(DRIVER_OPTIONS) } + +actions compile.c++.preprocess +{ + "$(CONFIG_COMMAND_CXX)" $(OPTIONS) $(USER_OPTIONS) -D$(SPACE)$(DEFINES) -I$(SPACE)"$(INCLUDES)" -E "$(>)" >"$(<)" $(DRIVER_OPTIONS) +} + +actions compile.c.preprocess +{ + "$(CONFIG_COMMAND_C)" $(OPTIONS) $(USER_OPTIONS) -D$(SPACE)$(DEFINES) -I$(SPACE)"$(INCLUDES)" -E "$(>)" >"$(<)" $(DRIVER_OPTIONS) +} + +# We don't want to invoke 'ld' (the linker) directly for 'link', since we +# want to give the CCE compiler driver a chance to modify the command line +# it passes to 'ld'. +# +# The question is: which CCE compiler driver do we use? The driver for C, +# the driver for C++, or the driver for Fortran? +# +# Here are things that definitely do not work: +# +# - Using the driver for C doesn't work when linking C++ programs, because +# things like 'std::cout' are not available in C, they are only +# available in C++. +# +# We use the driver for C++ below since we are primarily interested in +# compiling Boost, which is written in C++. Also, the C++ driver will +# properly link C code as well. + +actions link bind LIBRARIES +{ + "$(CONFIG_COMMAND_CXX)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,$(RPATH) -Wl,-rpath-link$(SPACE)-Wl,"$(RPATH_LINK)" -o "$(<)" $(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) $(DRIVER_OPTIONS) +} + +actions link.dll bind LIBRARIES +{ + "$(CONFIG_COMMAND_CXX)" -L"$(LINKPATH)" -Wl,$(RPATH_OPTION:E=-R)$(SPACE)-Wl,$(RPATH) -o "$(<[-1])" $(HAVE_SONAME)-Wl,$(SONAME_OPTION)$(SPACE)-Wl,$(<[-1]:D=) $(START-GROUP) "$(>)" "$(LIBRARIES)" $(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA) $(END-GROUP) $(OPTIONS) $(USER_OPTIONS) $(DRIVER_OPTIONS) +} + +actions piecemeal archive +{ + "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" + "$(.RANLIB)" "$(<)" +} + +### +### Updating rules +### + +# These are the actual updating rules that apply the associated actions +# when called. + +rule compile.c++ ( targets * : sources * : properties * ) +{ + compile-c++-procedure $(targets) : $(sources) : $(properties) ; +} + +rule compile.c ( targets * : sources * : properties * ) +{ + compile-c-procedure $(targets) : $(sources) : $(properties) ; +} + +rule compile.c++.preprocess ( targets * : sources * : properties * ) +{ + compile-c++-preprocess-procedure $(targets) : $(sources) : $(properties) ; +} + +rule compile.c.preprocess ( targets * : sources * : properties * ) +{ + compile-c-preprocess-procedure $(targets) : $(sources) : $(properties) ; +} + +rule link ( targets * : sources * : properties * ) +{ + link-procedure $(targets) : $(sources) : $(properties) ; +} + +rule link.dll ( targets * : sources * : properties * ) +{ + link-dll-procedure $(targets) : $(sources) : $(properties) ; +} + +rule archive ( targets * : sources * : properties * ) +{ + archive-procedure $(targets) : $(sources) : $(properties) ; +} + +# These are the procedure portions of the updating rules. Calling the +# procedure portion may modify the targets, but it will not apply actions +# to the targets. This allows us to reuse the procedure portions of the +# updating rules without applying the same actions to targets. + +rule compile-c++-procedure ( targets * : sources * : properties * ) +{ + set-cxxstd-procedure $(targets) : $(sources) : $(properties) ; + add-space-procedure $(targets) : $(sources) : $(properties) ; + debug-driver-procedure $(targets) : $(sources) : $(properties) ; +} + +rule compile-c-procedure ( targets * : sources * : properties * ) +{ + add-space-procedure $(targets) : $(sources) : $(properties) ; + debug-driver-procedure $(targets) : $(sources) : $(properties) ; +} + +rule compile-c++-preprocess-procedure ( targets * : sources * : properties * ) +{ + set-cxxstd-procedure $(targets) : $(sources) : $(properties) ; + add-space-procedure $(targets) : $(sources) : $(properties) ; + debug-driver-procedure $(targets) : $(sources) : $(properties) ; +} + +rule compile-c-preprocess-procedure ( targets * : sources * : properties * ) +{ + add-space-procedure $(targets) : $(sources) : $(properties) ; + debug-driver-procedure $(targets) : $(sources) : $(properties) ; +} + +rule link-procedure ( targets * : sources * : properties * ) +{ + set-cxxstd-procedure $(targets) : $(sources) : $(properties) ; + gcc-link-procedure $(targets) : $(sources) : $(properties) ; + debug-driver-procedure $(targets) : $(sources) : $(properties) ; + + # CCE driver command line flags for linking executables. + + local link = [ feature.get-values : $(properties) ] ; + switch $(link) + { + case shared : + DRIVER_OPTIONS on $(<) += -dynamic ; + case static : + DRIVER_OPTIONS on $(<) += -static ; + } + + # The link command line from the 'gcc' toolset includes: + # + # '$(FINDLIBS-ST-PFX) -l$(FINDLIBS-ST) $(FINDLIBS-SA-PFX) -l$(FINDLIBS-SA)' + # + # The 'FINDLIBS-ST' and 'FINDLIBS-SA' variables are the libraries + # specified by the '' and '' + # features, respectively. The 'FINDLIBS-ST-PFX' is typically + # '-Wl,-Bstatic'. The 'FINDLIBS-SA-PFX' is typically '-Wl,-Bdynamic'. + # + # The '-Bstatic' and '-Bdynamic' flags passed to the linker tell the + # linker how to link all of the following libraries. The flag is in effect + # until it is overridden by another '-B' flag on the command line. + # + # So, it makes sense that the 'gcc' toolset includes these flags, so the + # '' and '' libraries are linked + # properly. + # + # The last flag that is set ('-Bdynamic') affects the link type for any + # other libraries on the command line. In the 'gcc' toolset, this is okay, + # since there are no other libraries specified on the command line after + # these flags. However, when the CCE compiler driver invokes the linker, + # it adds additional libraries to the command line based on what modules + # are loaded in the environment. So, the last '-B' flag on the CCE driver + # command line affects the link type for all libraries that CCE + # automatically appends. + # + # Therefore, we have to set the final '-B' flag to the link type we want + # the CCE libraries to be linked with. Appending to the 'OPTIONS' variable + # seems reasonable. + + local link = [ feature.get-values : $(properties) ] ; + switch $(link) + { + case shared : + OPTIONS on $(<) += -Wl,-Bdynamic ; + case static : + OPTIONS on $(<) += -Wl,-Bstatic ; + } +} + +rule link-dll-procedure ( targets * : sources * : properties * ) +{ + set-cxxstd-procedure $(targets) : $(sources) : $(properties) ; + gcc-link-dll-procedure $(targets) : $(sources) : $(properties) ; + debug-driver-procedure $(targets) : $(sources) : $(properties) ; + + # CCE driver command line flags for linking shared libraries. + + DRIVER_OPTIONS on $(<) += -shared ; +} + +rule archive-procedure ( targets * : sources * : properties * ) +{ + gcc-archive-procedure $(targets) : $(sources) : $(properties) ; + debug-driver-procedure $(targets) : $(sources) : $(properties) ; +} + +# Utility procedure rules intended to be called from updating rules. + +rule gcc-link-procedure ( targets * : sources * : properties * ) +{ + + # Copied from 'gcc.jam'. + + SPACE on $(targets) = " " ; + # Serialize execution of the 'link' action, since running N links in + # parallel is just slower. For now, serialize only gcc links, it might be a + # good idea to serialize all links. + JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; + gcc.quote-rpath $(targets) ; +} + +rule gcc-link-dll-procedure ( targets * : sources * : properties * ) +{ + + # Copied from 'gcc.jam'. + + SPACE on $(targets) = " " ; + JAM_SEMAPHORE on $(targets) = gcc-link-semaphore ; + gcc.quote-rpath $(targets) ; +} + +rule gcc-archive-procedure ( targets * : sources * : properties * ) +{ + + # Copied from 'gcc.jam'. + + # Always remove archive and start again. Here is the rationale from + # + # Andre Hentz: + # + # I had a file, say a1.c, that was included into liba.a. I moved a1.c to + # a2.c, updated my Jamfiles and rebuilt. My program was crashing with absurd + # errors. After some debugging I traced it back to the fact that a1.o was + # *still* in liba.a + # + # Rene Rivera: + # + # Originally removing the archive was done by splicing an RM onto the + # archive action. That makes archives fail to build on NT when they have + # many files because it will no longer execute the action directly and blow + # the line length limit. Instead we remove the file in a different action, + # just before building the archive. + # + local clean.a = $(targets[1])(clean) ; + TEMPORARY $(clean.a) ; + NOCARE $(clean.a) ; + LOCATE on $(clean.a) = [ on $(targets[1]) return $(LOCATE) ] ; + DEPENDS $(clean.a) : $(sources) ; + DEPENDS $(targets) : $(clean.a) ; + common.RmTemps $(clean.a) : $(targets) ; +} + +rule add-space-procedure ( targets * : sources * : properties * ) +{ + SPACE on $(targets) = " " ; +} + +rule set-cxxstd-procedure ( targets * : sources * : properties * ) +{ + + # Translate '' into a standard recognized by CCE. + + local version = [ on $(targets[1]) return $(VERSION) ] ; + + local cxxstd = [ feature.get-values cxxstd : $(properties) ] ; + local cray-cxxstd = ; + + local unsupported-values = 2a ; # I don't know what '2a' means. + if $(cxxstd) && $(cxxstd) in $(unsupported-values) + { + + log-warning cray : ignoring unsupported property '$(cxxstd)' ; + + # Set to default value, or blank if default is unsupported. + + local default-value = [ get-default-feature-value cxxstd ] ; + if $(default-value) in $(unsupported-values) + { + cxxstd = ; + } + else + { + cxxstd = $(default-value) ; + } + } + + switch $(cxxstd) + { + case 98 : cray-cxxstd = 03 ; + case 03 : cray-cxxstd = 03 ; + case 0x : cray-cxxstd = 11 ; + case 11 : cray-cxxstd = 11 ; + case 1y : cray-cxxstd = 14 ; + case 14 : cray-cxxstd = 14 ; + case 1z : cray-cxxstd = 17 ; + case 17 : cray-cxxstd = 17 ; + case latest : + cray-cxxstd = [ latest-cray-cxxstd $(version) ] ; + } + + # If the 'cray-cxxstd' is not supported by this compiler version, we just + # let the command line fail. + + # If 'cxxstd' was blank, then 'cray-cxxstd' is also blank, and nothing is + # added to the command line. The compiler just uses it's default C++ + # standard. + + # Apply final options. + local space = " " ; + OPTIONS on $(targets) += -h$(space)std=c++$(cray-cxxstd) ; + + set-cxxstd-dialect-procedure $(targets) : $(sources) : $(properties) ; +} + +rule set-cxxstd-dialect-procedure ( targets * : sources * : properties * ) +{ + + # Translate '' into '-h [no]conform' and '-h [no]gnu' + # options. + + local version = [ on $(targets[1]) return $(VERSION) ] ; + + local cxxstd-dialect = [ feature.get-values cxxstd-dialect : $(properties) ] ; + local cray-conform = ; + local cray-gnu = ; + + local unsupported-values = ms ; + if $(cxxstd-dialect) && $(cxxstd-dialect) in $(unsupported-values) + { + + log-warning cray : ignoring unsupported property '$(cxxstd-dialect)' ; + + # Set to default value, or blank if default is unsupported. + + local default-value = [ get-default-feature-value cxxstd-dialect ] ; + if $(default-value) in $(unsupported-values) + { + cxxstd-dialect = ; + } + else + { + cxxstd-dialect = $(default-value) ; + } + } + + switch $(cxxstd-dialect) + { + case gnu : cray-conform = noconform ; + cray-gnu = gnu ; + case iso : cray-conform = conform ; + cray-gnu = nognu ; + } + + if [ has-conform-option $(version) ] = false + { + # The '-h [no]conform' option is ignored in recent versions of CCE. + cray-conform = ; + } + + # If 'cxxstd-dialect' was blank, then 'cray-conform' and 'cray-gnu' are + # also blank, and nothing is added to the command line. The compiler just + # uses it's default C++ dialect. + + # Apply final options. + local space = " " ; + OPTIONS on $(targets) += -h$(space)$(cray-conform) + -h$(space)$(cray-gnu) ; +} + +rule debug-driver-procedure ( targets * : sources * : properties * ) +{ + if $(.debug-driver) + { + + # Passing '-craype-verbose' to the CCE driver causes it to output the + # command lines for the underlying compiler that it invokes. + + DRIVER_OPTIONS on $(<) += -craype-verbose ; + } +} + +### +### Generators +### + +class cray-linking-generator : gcc-linking-generator +{ + rule action-class ( ) + { + return action ; + } +} + +# We reuse some generator classes from the 'unix' toolset. Specifically, +# we are reusing generators for the following updating actions: +# +# - 'archive' +# - 'searched-lib-generator' +# - 'prebuilt' +# +# Inheriting these generators is like using the same generator classes as +# the 'unix' toolset, but pointing them to the 'cray' updating rules. + +toolset.inherit-generators cray : unix : unix.link unix.link.dll ; + +# The 'C-compiling-generator' class adds source paths to the '' +# property. + +generators.register [ new C-compiling-generator + cray.compile.c++ + : CPP + : OBJ + : cray ] ; +generators.register [ new C-compiling-generator + cray.compile.c + : C + : OBJ + : cray ] ; +generators.register [ new C-compiling-generator + cray.compile.c++.preprocess + : CPP + : PREPROCESSED_CPP + : cray ] ; +generators.register [ new C-compiling-generator + cray.compile.c.preprocess + : C + : PREPROCESSED_C + : cray ] ; +generators.register [ new cray-linking-generator + cray.link + : LIB OBJ + : EXE + : cray ] ; +generators.register [ new cray-linking-generator + cray.link.dll + : LIB OBJ + : SHARED_LIB + : cray ] ; + +# Tell Boost.Build to prefer 'cray' generators over other valid +# generators. This is used to resolve a tie when Boost.Build finds that +# there is more than one viable generator for a particular build request. + +generators.override cray.prebuilt : builtin.prebuilt ; +generators.override cray.searched-lib-generator : searched-lib-generator ; + +type.set-generated-target-suffix PREPROCESSED_CPP : cray : i ; +type.set-generated-target-suffix PREPROCESSED_C : cray : i ; + +### +### Utility rules +### + +rule validate-command ( toolset command ) +{ + local found-command = [ common.find-tool $(command) ] ; + if $(found-command) && $(.debug-configuration) + { + log-notice $(toolset) : command '$(command)' found at [ common.get-absolute-tool-path $(found-command) ] ; + } + if ! $(found-command) + { + log-warning $(toolset) : command '$(command)' not found ; + found-command = $(command) ; + } + return $(found-command) ; +} + +local rule options-helper ( rule-or-module variable-name condition * : feature options * ) +{ + toolset.flags $(rule-or-module) $(variable-name) $(condition) : [ feature.get-values $(feature) : $(options) ] : unchecked ; +} + +rule handle-options ( + toolset + : toolset-condition * + : command-c command-c++ command-fortran command-ar command-ranlib + : options * +) +{ + + # Configures some common 'toolset.flags' options. In particular, this rule + # sets the compiler command name to invoke. Inspired by + # 'common.handle-options'. + + # We cannot use a single 'CONFIG_COMMAND' variable because each CCE driver + # can only handle a single source code language. Therefore, we have to + # give actions a way to specify which driver they intend to use, and we + # accomplish this by providing multiple 'CONFIG_COMMAND' variables to the + # action. We cannot set the language through a flag in the 'OPTIONS' + # variable the way the 'gcc' toolset does. + + toolset.flags $(toolset) CONFIG_COMMAND_C $(toolset-condition) : $(command-c) : unchecked ; + toolset.flags $(toolset) CONFIG_COMMAND_CXX $(toolset-condition) : $(command-c++) : unchecked ; + toolset.flags $(toolset) CONFIG_COMMAND_FORTRAN $(toolset-condition) : $(command-fortran) : unchecked ; + toolset.flags $(toolset).archive .AR $(toolset-condition) : $(command-ar) : unchecked ; + toolset.flags $(toolset).archive .RANLIB $(toolset-condition) : $(command-ranlib) : unchecked ; + + # The following flags are applied to all targets built by this + # configuration of this toolset. This particular configuration of this + # toolset is identified by '$(toolset-condition)'. This allows the user to + # specify 'options' in their 'using' statement, and those options will be + # applied to all targets built by this configuration of this toolset. + + options-helper $(toolset).compile USER_OPTIONS $(toolset-condition) : $(options) ; + options-helper $(toolset).compile USER_OPTIONS $(toolset-condition) : $(options) ; + options-helper $(toolset).compile.c++ USER_OPTIONS $(toolset-condition) : $(options) ; + options-helper $(toolset).compile.fortran USER_OPTIONS $(toolset-condition) : $(options) ; + options-helper $(toolset).compile.asm USER_OPTIONS $(toolset-condition) : $(options) ; + options-helper $(toolset).compile DEFINES $(toolset-condition) : $(options) ; + options-helper $(toolset).compile INCLUDES $(toolset-condition) : $(options) ; + + options-helper $(toolset).link USER_OPTIONS $(toolset-condition) : $(options) ; + options-helper $(toolset).link LINKPATH $(toolset-condition) : $(options) ; + options-helper $(toolset).link FINDLIBS-ST $(toolset-condition) : $(options) ; + options-helper $(toolset).link FINDLIBS-SA $(toolset-condition) : $(options) ; + options-helper $(toolset).link LIBRARIES $(toolset-condition) : $(options) ; + + options-helper $(toolset).archive AROPTIONS $(toolset-condition) : $(options) ; +} + +rule latest-cray-cxxstd ( compiler-version ) +{ + # Select latest 'cray-cxxstd' based on compiler version. + + local cray-cxxstd = 03 ; + + if $(compiler-version) >= [ numeric-version 8.6 ] + { + cray-cxxstd = 14 ; + } + + return $(cray-cxxstd) ; +} + +rule has-conform-option ( compiler-version ) +{ + + # Returns 'true' or 'false'. Returns empty list if the 'compiler-version' + # is not supported. + + local result = true ; + + if $(compiler-version) >= [ numeric-version 8.6 ] + { + result = false ; + } + + return $(result) ; +} + +local rule justify-right ( pad-char elements * ) +{ + + # Returns a list of 'elements' where each 'element' is at least 2 + # characters long. If an 'element' is less than two characters long, pads + # 'element' with 'pad-char' to make it 2 characters long. + + local result = ; + local p = $(pad-char) ; + for local e in $(elements) + { + switch $(e) + { + case ?? : result += $(e) ; + case ? : result += $(p)$(e) ; + case * : result += $(p)$(p) ; + } + } + return $(result) ; +} + +local rule list-justify-left ( pad-elem elements * ) +{ + + # Add 'pad-elem' to 'elements' list until it has 4 elements. If 'elements' + # list already had 4 or more elements, returns the first 4 elements in + # 'elements' list. + + local tally = x ; + local result = ; + for local e in $(elements) + { + if $(tally) != xxxxx + { + result += $(e) ; + tally = $(tally)x ; + } + } + + while $(tally) != xxxxx + { + result += $(pad-elem) ; + tally = $(tally)x ; + } + + return $(result) ; +} + +local rule numeric-version ( dotted-version ) +{ + + # Returns a numeric representation of version that can be compared + # directly with comparison operators. + + local result = [ regex.split $(dotted-version) "[.]" ] ; + result = [ list-justify-left 0 $(result) ] ; + result = [ justify-right 0 $(result) ] ; + result = $(result:J="") ; + + return $(result) ; +} + +local rule get-default-feature-value ( feature-name ) +{ + local default-property = [ feature.defaults $(feature-name) ] ; + local default-value = [ feature.get-values $(feature-name) : $(default-property) ] ; + return $(default-value) ; +} + +rule log ( log-level prefix ? : * ) +{ + for local message-arg in 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 + { + local message = $($(message-arg)) ; + if $(message) + { + ECHO $(log-level): $(prefix): $(message) ; + } + } +} + +rule log-error ( prefix ? : * ) +{ + log error $(prefix) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : $(18) : $(19) : $(20) : $(21) : $(22) : $(23) : $(24) ; +} + +rule log-warning ( prefix ? : * ) +{ + log warning $(prefix) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : $(18) : $(19) : $(20) : $(21) : $(22) : $(23) : $(24) ; +} + +rule log-notice ( prefix ? : * ) +{ + log notice $(prefix) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : $(18) : $(19) : $(20) : $(21) : $(22) : $(23) : $(24) ; +} + +rule __test__ ( ) +{ + import assert ; + + assert.result 08060000 : numeric-version 8.6 ; + assert.result 08061500 : numeric-version 8.6.15 ; + assert.result 08061501 : numeric-version 8.6.15.1 ; + assert.result 08061501 : numeric-version 8.6.15.1.2 ; + + local a = [ numeric-version 8.6 ] ; + local b = [ numeric-version 8.5.9 ] ; + + # 'assert.equal x : y' forces the test to fail. It's like saying 'assert + # false'. + + if ! ( $(a) > $(b) ) + { + assert.equal x : y ; + } + + if ! ( $(b) < $(a) ) + { + assert.equal x : y ; + } + + if ! ( $(a) >= $(b) ) + { + assert.equal x : y ; + } + + if ! ( $(a) >= $(a) ) + { + assert.equal x : y ; + } + + if ! ( $(b) <= $(a) ) + { + assert.equal x : y ; + } + + if ! ( $(b) <= $(b) ) + { + assert.equal x : y ; + } + + if ! ( $(a) = $(a) ) + { + assert.equal x : y ; + } + + if ! ( $(a) != $(b) ) + { + assert.equal x : y ; + } +} +