mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 13:22:11 +00:00
Some MPI implementation report -lpthread in the compilation flags. Previously, we'd add -lpthread, but did not set threading=multi, as we really should. One manifestation of this issue is that when building with such an MPI, and requesting "threading=single threading=multi", we'd actually try to build two variants of everything, including "mpi.so" Python extension. And since Python extensions, at present, do not have their name adjusted for different variants, we'd get duplicate target name error. [SVN r57791]
521 lines
17 KiB
Plaintext
521 lines
17 KiB
Plaintext
# Support for the Message Passing Interface (MPI)
|
|
#
|
|
# (C) Copyright 2005, 2006 Trustees of Indiana University
|
|
# (C) Copyright 2005 Douglas Gregor
|
|
#
|
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
|
|
#
|
|
# Authors: Douglas Gregor
|
|
# Andrew Lumsdaine
|
|
#
|
|
# ==== MPI Configuration ====
|
|
#
|
|
# For many users, MPI support can be enabled simply by adding the following
|
|
# line to your user-config.jam file:
|
|
#
|
|
# using mpi ;
|
|
#
|
|
# This should auto-detect MPI settings based on the MPI wrapper compiler in
|
|
# your path, e.g., "mpic++". If the wrapper compiler is not in your path, or
|
|
# has a different name, you can pass the name of the wrapper compiler as the
|
|
# first argument to the mpi module:
|
|
#
|
|
# using mpi : /opt/mpich2-1.0.4/bin/mpiCC ;
|
|
#
|
|
# If your MPI implementation does not have a wrapper compiler, or the MPI
|
|
# auto-detection code does not work with your MPI's wrapper compiler,
|
|
# you can pass MPI-related options explicitly via the second parameter to the
|
|
# mpi module:
|
|
#
|
|
# using mpi : : <find-shared-library>lammpio <find-shared-library>lammpi++
|
|
# <find-shared-library>mpi <find-shared-library>lam
|
|
# <find-shared-library>dl ;
|
|
#
|
|
# To see the results of MPI auto-detection, pass "--debug-configuration" on
|
|
# the bjam command line.
|
|
#
|
|
# The (optional) fourth argument configures Boost.MPI for running
|
|
# regression tests. These parameters specify the executable used to
|
|
# launch jobs (default: "mpirun") followed by any necessary arguments
|
|
# to this to run tests and tell the program to expect the number of
|
|
# processors to follow (default: "-np"). With the default parameters,
|
|
# for instance, the test harness will execute, e.g.,
|
|
#
|
|
# mpirun -np 4 all_gather_test
|
|
#
|
|
# ==== Linking Against the MPI Libraries ===
|
|
#
|
|
# To link against the MPI libraries, import the "mpi" module and add the
|
|
# following requirement to your target:
|
|
#
|
|
# <library>/mpi//mpi
|
|
#
|
|
# Since MPI support is not always available, you should check
|
|
# "mpi.configured" before trying to link against the MPI libraries.
|
|
|
|
import "class" : new ;
|
|
import common ;
|
|
import feature : feature ;
|
|
import generators ;
|
|
import os ;
|
|
import project ;
|
|
import property ;
|
|
import testing ;
|
|
import toolset ;
|
|
import type ;
|
|
|
|
# Make this module a project
|
|
project.initialize $(__name__) ;
|
|
project mpi ;
|
|
|
|
if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
|
|
{
|
|
.debug-configuration = true ;
|
|
}
|
|
|
|
# Assuming the first part of the command line is the given prefix
|
|
# followed by some non-empty value, remove the first argument. Returns
|
|
# either nothing (if there was no prefix or no value) or a pair
|
|
#
|
|
# <name>value rest-of-cmdline
|
|
#
|
|
# This is a subroutine of cmdline_to_features
|
|
rule add_feature ( prefix name cmdline )
|
|
{
|
|
local match = [ MATCH "^$(prefix)([^\" ]+|\"[^\"]+\") *(.*)$" : $(cmdline) ] ;
|
|
|
|
# If there was no value associated with the prefix, abort
|
|
if ! $(match) {
|
|
return ;
|
|
}
|
|
|
|
local value = $(match[1]) ;
|
|
|
|
if [ MATCH " +" : $(value) ] {
|
|
value = "\"$(value)\"" ;
|
|
}
|
|
|
|
return "<$(name)>$(value)" $(match[2]) ;
|
|
}
|
|
|
|
# Strip any end-of-line characters off the given string and return the
|
|
# result.
|
|
rule strip-eol ( string )
|
|
{
|
|
local match = [ MATCH "^(([A-Za-z0-9~`\.!@#$%^&*()_+={};:'\",.<>/?\\| -]|[|])*).*$" : $(string) ] ;
|
|
|
|
if $(match)
|
|
{
|
|
return $(match[1]) ;
|
|
}
|
|
else
|
|
{
|
|
return $(string) ;
|
|
}
|
|
}
|
|
|
|
# Split a command-line into a set of features. Certain kinds of
|
|
# compiler flags are recognized (e.g., -I, -D, -L, -l) and replaced
|
|
# with their Boost.Build equivalents (e.g., <include>, <define>,
|
|
# <library-path>, <find-library>). All other arguments are introduced
|
|
# using the features in the unknown-features parameter, because we
|
|
# don't know how to deal with them. For instance, if your compile and
|
|
# correct. The incoming command line should be a string starting with
|
|
# an executable (e.g., g++ -I/include/path") and may contain any
|
|
# number of command-line arguments thereafter. The result is a list of
|
|
# features corresponding to the given command line, ignoring the
|
|
# executable.
|
|
rule cmdline_to_features ( cmdline : unknown-features ? )
|
|
{
|
|
local executable ;
|
|
local features ;
|
|
local otherflags ;
|
|
local result ;
|
|
|
|
unknown-features ?= <cxxflags> <linkflags> ;
|
|
|
|
# Pull the executable out of the command line. At this point, the
|
|
# executable is just thrown away.
|
|
local match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$" : $(cmdline) ] ;
|
|
executable = $(match[1]) ;
|
|
cmdline = $(match[2]) ;
|
|
|
|
# List the prefix/feature pairs that we will be able to transform.
|
|
# Every kind of parameter not mentioned here will be placed in both
|
|
# cxxflags and linkflags, because we don't know where they should go.
|
|
local feature_kinds-D = "define" ;
|
|
local feature_kinds-I = "include" ;
|
|
local feature_kinds-L = "library-path" ;
|
|
local feature_kinds-l = "find-shared-library" ;
|
|
|
|
while $(cmdline) {
|
|
|
|
# Check for one of the feature prefixes we know about. If we
|
|
# find one (and the associated value is nonempty), convert it
|
|
# into a feature.
|
|
local match = [ MATCH "^(-.)(.*)" : $(cmdline) ] ;
|
|
local matched ;
|
|
if $(match) && $(match[2]) {
|
|
local prefix = $(match[1]) ;
|
|
if $(feature_kinds$(prefix)) {
|
|
local name = $(feature_kinds$(prefix)) ;
|
|
local add = [ add_feature $(prefix) $(name) $(cmdline) ] ;
|
|
|
|
if $(add) {
|
|
|
|
if $(add[1]) = <find-shared-library>pthread
|
|
{
|
|
# Uhm. It's not really nice that this MPI implementation
|
|
# uses -lpthread as opposed to -pthread. We do want to
|
|
# set <threading>multi, instead of -lpthread.
|
|
result += "<threading>multi" ;
|
|
MPI_EXTRA_REQUIREMENTS += "<threading>multi" ;
|
|
}
|
|
else
|
|
{
|
|
result += $(add[1]) ;
|
|
}
|
|
|
|
cmdline = $(add[2]) ;
|
|
matched = yes ;
|
|
}
|
|
}
|
|
}
|
|
|
|
# If we haven't matched a feature prefix, just grab the command-line
|
|
# argument itself. If we can map this argument to a feature
|
|
# (e.g., -pthread -> <threading>multi), then do so; otherwise,
|
|
# and add it to the list of "other" flags that we don't
|
|
# understand.
|
|
if ! $(matched) {
|
|
match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$" : $(cmdline) ] ;
|
|
local value = $(match[1]) ;
|
|
cmdline = $(match[2]) ;
|
|
|
|
# Check for multithreading support
|
|
if $(value) = "-pthread" || $(value) = "-pthreads"
|
|
{
|
|
result += "<threading>multi" ;
|
|
|
|
# DPG: This is a hack intended to work around a BBv2 bug where
|
|
# requirements propagated from libraries are not checked for
|
|
# conflicts when BBv2 determines which "common" properties to
|
|
# apply to a target. In our case, the <threading>single property
|
|
# gets propagated from the common properties to Boost.MPI
|
|
# targets, even though <threading>multi is in the usage
|
|
# requirements of <library>/mpi//mpi.
|
|
MPI_EXTRA_REQUIREMENTS += "<threading>multi" ;
|
|
}
|
|
else if [ MATCH "(.*[a-zA-Z0-9<>?-].*)" : $(value) ] {
|
|
otherflags += $(value) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
# If there are other flags that we don't understand, add them to the
|
|
# result as both <cxxflags> and <linkflags>
|
|
if $(otherflags) {
|
|
for unknown in $(unknown-features)
|
|
{
|
|
result += "$(unknown)$(otherflags)" ;
|
|
}
|
|
}
|
|
|
|
return $(result) ;
|
|
}
|
|
|
|
# Determine if it is safe to execute the given shell command by trying
|
|
# to execute it and determining whether the exit code is zero or
|
|
# not. Returns true for an exit code of zero, false otherwise.
|
|
local rule safe-shell-command ( cmdline )
|
|
{
|
|
local result = [ SHELL "$(cmdline) > /dev/null 2>/dev/null; if [ "$?" -eq "0" ]; then echo SSCOK; fi" ] ;
|
|
return [ MATCH ".*(SSCOK).*" : $(result) ] ;
|
|
}
|
|
|
|
# Initialize the MPI module.
|
|
rule init ( mpicxx ? : options * : mpirun-with-options * )
|
|
{
|
|
if ! $(options) && $(.debug-configuration)
|
|
{
|
|
ECHO "===============MPI Auto-configuration===============" ;
|
|
}
|
|
|
|
if ! $(mpicxx) && [ os.on-windows ]
|
|
{
|
|
# Try to auto-configure to the Microsoft Compute Cluster Pack
|
|
local cluster_pack_path_native = "C:\\Program Files\\Microsoft Compute Cluster Pack" ;
|
|
local cluster_pack_path = [ path.make $(cluster_pack_path_native) ] ;
|
|
if [ GLOB $(cluster_pack_path_native)\\Include : mpi.h ]
|
|
{
|
|
if $(.debug-configuration)
|
|
{
|
|
ECHO "Found Microsoft Compute Cluster Pack: $(cluster_pack_path_native)" ;
|
|
}
|
|
|
|
# Pick up either the 32-bit or 64-bit library, depending on which address
|
|
# model the user has selected. Default to 32-bit.
|
|
options = <include>$(cluster_pack_path)/Include
|
|
<address-model>64:<library-path>$(cluster_pack_path)/Lib/amd64
|
|
<library-path>$(cluster_pack_path)/Lib/i386
|
|
<find-static-library>msmpi
|
|
<toolset>msvc:<define>_SECURE_SCL=0
|
|
;
|
|
|
|
# Setup the "mpirun" equivalent (mpiexec)
|
|
.mpirun = "\"$(cluster_pack_path_native)\\Bin\\mpiexec.exe"\" ;
|
|
.mpirun_flags = -n ;
|
|
}
|
|
else if $(.debug-configuration)
|
|
{
|
|
ECHO "Did not find Microsoft Compute Cluster Pack in $(cluster_pack_path_native)." ;
|
|
}
|
|
}
|
|
|
|
if ! $(options)
|
|
{
|
|
# Try to auto-detect options based on the wrapper compiler
|
|
local command = [ common.get-invocation-command mpi : mpic++ : $(mpicxx) ] ;
|
|
|
|
if ! $(mpicxx) && ! $(command)
|
|
{
|
|
# Try "mpiCC", which is used by MPICH
|
|
command = [ common.get-invocation-command mpi : mpiCC ] ;
|
|
}
|
|
|
|
if ! $(mpicxx) && ! $(command)
|
|
{
|
|
# Try "mpicxx", which is used by OpenMPI and MPICH2
|
|
command = [ common.get-invocation-command mpi : mpicxx ] ;
|
|
}
|
|
|
|
local result ;
|
|
local compile_flags ;
|
|
local link_flags ;
|
|
|
|
if ! $(command)
|
|
{
|
|
# Do nothing: we'll complain later
|
|
}
|
|
# OpenMPI and newer versions of LAM-MPI have -showme:compile and
|
|
# -showme:link.
|
|
else if [ safe-shell-command "$(command) -showme:compile" ] &&
|
|
[ safe-shell-command "$(command) -showme:link" ]
|
|
{
|
|
if $(.debug-configuration)
|
|
{
|
|
ECHO "Found recent LAM-MPI or Open MPI wrapper compiler: $(command)" ;
|
|
}
|
|
|
|
compile_flags = [ SHELL "$(command) -showme:compile" ] ;
|
|
link_flags = [ SHELL "$(command) -showme:link" ] ;
|
|
|
|
# Prepend COMPILER as the executable name, to match the format of
|
|
# other compilation commands.
|
|
compile_flags = "COMPILER $(compile_flags)" ;
|
|
link_flags = "COMPILER $(link_flags)" ;
|
|
}
|
|
# Look for LAM-MPI's -showme
|
|
else if [ safe-shell-command "$(command) -showme" ]
|
|
{
|
|
if $(.debug-configuration)
|
|
{
|
|
ECHO "Found older LAM-MPI wrapper compiler: $(command)" ;
|
|
}
|
|
|
|
result = [ SHELL "$(command) -showme" ] ;
|
|
}
|
|
# Look for MPICH
|
|
else if [ safe-shell-command "$(command) -show" ]
|
|
{
|
|
if $(.debug-configuration)
|
|
{
|
|
ECHO "Found MPICH wrapper compiler: $(command)" ;
|
|
}
|
|
compile_flags = [ SHELL "$(command) -compile_info" ] ;
|
|
link_flags = [ SHELL "$(command) -link_info" ] ;
|
|
}
|
|
|
|
if $(result) || $(compile_flags) && $(link_flags)
|
|
{
|
|
if $(result)
|
|
{
|
|
result = [ strip-eol $(result) ] ;
|
|
options = [ cmdline_to_features $(result) ] ;
|
|
}
|
|
else
|
|
{
|
|
compile_flags = [ strip-eol $(compile_flags) ] ;
|
|
link_flags = [ strip-eol $(link_flags) ] ;
|
|
|
|
# Separately process compilation and link features, then combine
|
|
# them at the end.
|
|
local compile_features = [ cmdline_to_features $(compile_flags)
|
|
: "<cxxflags>" ] ;
|
|
local link_features = [ cmdline_to_features $(link_flags)
|
|
: "<linkflags>" ] ;
|
|
options = $(compile_features) $(link_features) ;
|
|
}
|
|
|
|
# If requested, display MPI configuration information.
|
|
if $(.debug-configuration)
|
|
{
|
|
if $(result)
|
|
{
|
|
ECHO " Wrapper compiler command line: $(result)" ;
|
|
}
|
|
else
|
|
{
|
|
local match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$"
|
|
: $(compile_flags) ] ;
|
|
ECHO "MPI compilation flags: $(match[2])" ;
|
|
local match = [ MATCH "^([^\" ]+|\"[^\"]+\") *(.*)$"
|
|
: $(link_flags) ] ;
|
|
ECHO "MPI link flags: $(match[2])" ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if $(command)
|
|
{
|
|
ECHO "MPI auto-detection failed: unknown wrapper compiler $(command)" ;
|
|
ECHO "Please report this error to the Boost mailing list: http://www.boost.org" ;
|
|
}
|
|
else if $(mpicxx)
|
|
{
|
|
ECHO "MPI auto-detection failed: unable to find wrapper compiler $(mpicxx)" ;
|
|
}
|
|
else
|
|
{
|
|
ECHO "MPI auto-detection failed: unable to find wrapper compiler `mpic++' or `mpiCC'" ;
|
|
}
|
|
ECHO "You will need to manually configure MPI support." ;
|
|
}
|
|
|
|
}
|
|
|
|
# Find mpirun (or its equivalent) and its flags
|
|
if ! $(.mpirun)
|
|
{
|
|
.mpirun =
|
|
[ common.get-invocation-command mpi : mpirun : $(mpirun-with-options[1]) ] ;
|
|
.mpirun_flags = $(mpirun-with-options[2-]) ;
|
|
.mpirun_flags ?= -np ;
|
|
}
|
|
|
|
if $(.debug-configuration)
|
|
{
|
|
if $(options)
|
|
{
|
|
echo "MPI build features: " ;
|
|
ECHO $(options) ;
|
|
}
|
|
|
|
if $(.mpirun)
|
|
{
|
|
echo "MPI launcher: $(.mpirun) $(.mpirun_flags)" ;
|
|
}
|
|
|
|
ECHO "====================================================" ;
|
|
}
|
|
|
|
if $(options)
|
|
{
|
|
.configured = true ;
|
|
|
|
# Set up the "mpi" alias
|
|
alias mpi : : : : $(options) ;
|
|
}
|
|
}
|
|
|
|
# States whether MPI has bee configured
|
|
rule configured ( )
|
|
{
|
|
return $(.configured) ;
|
|
}
|
|
|
|
# Returs the "extra" requirements needed to build MPI. These requirements are
|
|
# part of the /mpi//mpi library target, but they need to be added to anything
|
|
# that uses MPI directly to work around bugs in BBv2's propagation of
|
|
# requirements.
|
|
rule extra-requirements ( )
|
|
{
|
|
return $(MPI_EXTRA_REQUIREMENTS) ;
|
|
}
|
|
|
|
# Support for testing; borrowed from Python
|
|
type.register RUN_MPI_OUTPUT ;
|
|
type.register RUN_MPI : : TEST ;
|
|
|
|
class mpi-test-generator : generator
|
|
{
|
|
import property-set ;
|
|
|
|
rule __init__ ( * : * )
|
|
{
|
|
generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
|
self.composing = true ;
|
|
}
|
|
|
|
rule run ( project name ? : property-set : sources * : multiple ? )
|
|
{
|
|
# Generate an executable from the sources. This is the executable we will run.
|
|
local executable =
|
|
[ generators.construct $(project) $(name) : EXE : $(property-set) : $(sources) ] ;
|
|
|
|
result =
|
|
[ construct-result $(executable[2-]) : $(project) $(name)-run : $(property-set) ] ;
|
|
}
|
|
}
|
|
|
|
# Use mpi-test-generator to generate MPI tests from sources
|
|
generators.register
|
|
[ new mpi-test-generator mpi.capture-output : : RUN_MPI_OUTPUT ] ;
|
|
|
|
generators.register-standard testing.expect-success
|
|
: RUN_MPI_OUTPUT : RUN_MPI ;
|
|
|
|
# The number of processes to spawn when executing an MPI test.
|
|
feature mpi:processes : : free incidental ;
|
|
|
|
# The flag settings on testing.capture-output do not
|
|
# apply to mpi.capture output at the moment.
|
|
# Redo this explicitly.
|
|
toolset.flags mpi.capture-output ARGS <testing.arg> ;
|
|
rule capture-output ( target : sources * : properties * )
|
|
{
|
|
# Use the standard capture-output rule to run the tests
|
|
testing.capture-output $(target) : $(sources[1]) : $(properties) ;
|
|
|
|
# Determine the number of processes we should run on.
|
|
local num_processes = [ property.select <mpi:processes> : $(properties) ] ;
|
|
num_processes = $(num_processes:G=) ;
|
|
|
|
# We launch MPI processes using the "mpirun" equivalent specified by the user.
|
|
LAUNCHER on $(target) =
|
|
[ on $(target) return $(.mpirun) $(.mpirun_flags) $(num_processes) ] ;
|
|
}
|
|
|
|
# Creates a set of test cases to be run through the MPI launcher. The name, sources,
|
|
# and requirements are the same as for any other test generator. However, schedule is
|
|
# a list of numbers, which indicates how many processes each test run will use. For
|
|
# example, passing 1 2 7 will run the test with 1 process, then 2 processes, then 7
|
|
# 7 processes. The name provided is just the base name: the actual tests will be
|
|
# the name followed by a hypen, then the number of processes.
|
|
rule mpi-test ( name : sources * : requirements * : schedule * )
|
|
{
|
|
sources ?= $(name).cpp ;
|
|
schedule ?= 1 2 3 4 7 8 13 17 ;
|
|
|
|
local result ;
|
|
for processes in $(schedule)
|
|
{
|
|
result += [ testing.make-test
|
|
run-mpi : $(sources) /boost/mpi//boost_mpi
|
|
: $(requirements) <toolset>msvc:<link>static <mpi:processes>$(processes) : $(name)-$(processes) ] ;
|
|
}
|
|
return $(result) ;
|
|
}
|