Repository: spack/spack

Issue:
Installation issue: boost
While trying out a recent PR to address boost%intel build issues, I encountered another failure:
```
==> [2021-09-17-11:25:19.231164] './b2' '--clean' '-j' '16' '--user-config=/tmp/quellyn/spack-stage/spack-stage-boost-1.77.0-ejpxz3b52lknwmoglt4sj6nyk4ugzmhd/spack-src/user-config.jam' 'variant=release' '--disable-icu' '-s' 'BZIP2_INCLUDE=/yellow/users/quellyn/GIT/spack/opt/spack/linux-rhel7-broadwell/intel-19.0.4.243/bzip2-1.0.8-m7cxnmcfzpinzt6c5vadhctn2pmp7yvz/include' '-s' 'BZIP2_LIBPATH=/yellow/users/quellyn/GIT/spack/opt/spack/linux-rhel7-broadwell/intel-19.0.4.243/bzip2-1.0.8-m7cxnmcfzpinzt6c5vadhctn2pmp7yvz/lib' '-s' 'ZLIB_INCLUDE=/yellow/users/quellyn/GIT/spack/opt/spack/linux-rhel7-broadwell/intel-19.0.4.243/zlib-1.2.11-hzlvjhwmvvlcx6mtbpzahf2tz2wod6lz/include' '-s' 'ZLIB_LIBPATH=/yellow/users/quellyn/GIT/spack/opt/spack/linux-rhel7-broadwell/intel-19.0.4.243/zlib-1.2.11-hzlvjhwmvvlcx6mtbpzahf2tz2wod6lz/lib' '-s' 'NO_LZMA=1' '-s' 'NO_ZSTD=1' 'link=static,shared' '--layout=system' 'toolset=intel-linux' 'cxxstd=14' 'cxxflags="-fPIC"' 'visibility=hidden'
/tmp/quellyn/spack-stage/spack-stage-boost-1.77.0-ejpxz3b52lknwmoglt4sj6nyk4ugzmhd/spack-src/tools/build/src/tools/intel-linux.jam:96: in intel-linux.init
*** argument error
* rule common.get-absolute-tool-path ( command )
* called with: (  )
* missing argument command
/tmp/quellyn/spack-stage/spack-stage-boost-1.77.0-ejpxz3b52lknwmoglt4sj6nyk4ugzmhd/spack-src/tools/build/src/tools/common.jam:329:see definition of rule 'common.get-absolute-tool-path' being called
```
Here's my [spack-build-out.txt](https://github.com/spack/spack/files/7187510/spack-build-out.txt)

_Originally posted by @quellyn in https://github.com/spack/spack/issues/25965#issuecomment-921977611_


Discussion:
Mentioning @scheibelp, who asked me to post my issue separately.
can reproduce with `intel-parallel-studio@cluster.2020.4`
I think the problem is located here in the `intel-linux.jam`:

```
# Intel oneAPI 2020, and onward.
.bin(oneAPI) =
    [ path.join $(.home) intel/oneapi/compiler/latest/linux/bin ]
    /opt/intel/oneapi/compiler/latest/linux/bin ;
# Intel C++ Composer XE 2011 for Linux, aka Intel C++ Compiler XE 12.0,
# aka intel-linux-12.0.
.bin(12.0) = /opt/intel/bin ;
# Intel C++ Compiler 11.1.
.bin(11.1) = /opt/intel_cce_11.1.064.x86_64/bin ;
# Intel C++ Compiler 11.0.
.bin(11.0) = /opt/intel_cce_11.0.074.x86_64/bin ;
# Intel C++ Compiler 10.1.
.bin(10.1) = /opt/intel_cce_10.1.013_x64/bin ;
# Intel C++ Compiler 9.1.
.bin(9.1) = /opt/intel_cc_91/bin ;
# Intel C++ Compiler 9.0.
.bin(9.0) = /opt/intel_cc_90/bin ;
# Intel C++ Compiler 8.1.
.bin(8.1) = /opt/intel_cc_81/bin ;
# Intel C++ Compiler 8.0.
.bin(8.0) = /opt/intel_cc_80/bin ;

rule init ( version ? :  command * : options * )
{
    local user_version = [ MATCH "([0-9.]+)" : $(version) ] ;
    local user_command = $(command) ;
    if $(user_version)
    {
        user_version = [ regex.split $(user_version) "[.]" ] ;
    }

    local command_lib_path ;
    local detected_version ;
    local detected_command ;
    local command_abs_path ;
    if ! $(user_version) && ! $(user_command)
    {
        # If nothing given, try and discover the latest toolset available.
        if ! $(detected_command)
        {
            local bin_paths = $(.bin(oneAPI)) ;
            detected_command = [ common.find-tool icpx : $(bin_paths) ] ;
            command_abs_path = [ common.get-absolute-tool-path $(detected_command) ] ;
            command_lib_path = $(command_abs_path)/../compiler/lib/intel64 ;
        }
        if ! $(detected_command)
        {
            local bin_paths = $(.bin(12.0)) $(.bin(11.1)) $(.bin(11.0)) ;
            detected_command = [ common.find-tool icpc : $(bin_paths) ] ;
            command_abs_path = [ common.get-absolute-tool-path $(detected_command) ] ;
            command_lib_path = $(command_abs_path)/../lib/x86_64 ;
        }
        if ! $(detected_command)
        {
            local bin_paths = $(.bin(10.1)) $(.bin(9.1)) $(.bin(9.0))
                $(.bin(8.1)) $(.bin(8.0)) ;
            detected_command = [ common.find-tool icpc : $(bin_paths) ] ;
            command_abs_path = [ common.get-absolute-tool-path $(detected_command) ] ;
            command_lib_path = $(command_abs_path)/../lib ;
        }
        if $(detected_command)
        {
            local version_cmd = "LD_LIBRARY_PATH=$(command_lib_path) $(detected_command) --version" ;
            local version_output = [ SHELL $(version_cmd) ] ;
            detected_version = [ MATCH "([0-9.]+)" : $(version_output) ] ;
        }
    }
    else if $(user_command)
    {
        # If only a command given, determine the version from the command.
        # Note, we assume that the user command does everything needed to
        # property execute the command.
        local version_cmd = $(user_command:J=" ") ;
        local version_output = [ SHELL "$(version_cmd) --version" ] ;
        detected_command = $(user_command) ;
        detected_version = [ MATCH "([0-9.]+)" : $(version_output) ] ;
    }
...
```

For Intel we specifically do not set the `command` in the `user-config.jam`, hence Boost will try to auto-detect in well-known paths, which is a) wrong from a Spack perspective since it ignores our compiler and b) fails if the Intel compiler is at a different location.

Unfortunately simply setting the command in the `user-config.jam` did not help yet.

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/boost/package.py b/var/spack/repos/builtin/packages/boost/package.py
--- a/var/spack/repos/builtin/packages/boost/package.py
+++ b/var/spack/repos/builtin/packages/boost/package.py
@@ -333,6 +333,7 @@ def url_for_version(self, version):
     def determine_toolset(self, spec):
         toolsets = {'g++': 'gcc',
                     'icpc': 'intel',
+                    'icpx': 'intel',
                     'clang++': 'clang',
                     'armclang++': 'clang',
                     'xlc++': 'xlcpp',
@@ -343,6 +344,8 @@ def determine_toolset(self, spec):
 
         if spec.satisfies('@1.47:'):
             toolsets['icpc'] += '-linux'
+            toolsets['icpx'] += '-linux'
+
         for cc, toolset in toolsets.items():
             if cc in self.compiler.cxx_names:
                 return toolset
@@ -383,14 +386,8 @@ def determine_bootstrap_options(self, spec, with_libs, options):
         with open('user-config.jam', 'w') as f:
             # Boost may end up using gcc even though clang+gfortran is set in
             # compilers.yaml. Make sure this does not happen:
-            if not spec.satisfies('%intel'):
-                # using intel-linux : : spack_cxx in user-config.jam leads to
-                # error: at project-config.jam:12
-                # error: duplicate initialization of intel-linux with the following parameters:  # noqa
-                # error: version = <unspecified>
-                # error: previous initialization at ./user-config.jam:1
-                f.write("using {0} : : {1} ;\n".format(boost_toolset_id,
-                                                       spack_cxx))
+            f.write("using {0} : : {1} ;\n".format(boost_toolset_id,
+                                                   spack_cxx))
 
             if '+mpi' in spec:
                 # Use the correct mpi compiler.  If the compiler options are
@@ -572,6 +569,11 @@ def install(self, spec, prefix):
 
         bootstrap(*bootstrap_options)
 
+        # strip the toolchain to avoid double include errors (intel) or
+        # user-config being overwritten (again intel, but different boost version)
+        filter_file(r'^\s*using {0}.*'.format(self.determine_toolset(spec)),  '',
+                    os.path.join(self.stage.source_path, 'project-config.jam'))
+
         # b2 used to be called bjam, before 1.47 (sigh)
         b2name = './b2' if spec.satisfies('@1.47:') else './bjam'
 


Test patch: