"""
Builder.py, module definition of Builder class.
This is a class designed for controlling the building phase.
"""

# from __future__ import absolute_import
# from __future__ import division
# from __future__ import print_function
# from __future__ import unicode_literals
# Copyright (C) 2015  Stefano Zaghi
#
# This file is part of FoBiS.py.
#
# FoBiS.py is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# FoBiS.py is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with FoBiS.py. If not, see <http://www.gnu.org/licenses/>.
# from future import standard_library
# standard_library.install_aliases()
# from builtins import str
# from builtins import range
# from builtins import *
# from builtins import object
try:
    from multiprocessing import Pool

    __parallel__ = True
except ImportError:
    print("Module 'multiprocessing' not found: parallel compilation disabled")
    __parallel__ = False
import operator
import os

from .Compiler import Compiler
from .utils import check_results, print_fake, safe_mkdir, syswork


class Builder:
    """Builder is an object that handles the building system, its attributes and methods."""

    def __init__(self, cliargs, print_n=None, print_w=None):
        """
        Parameters
        ----------
        cliargs : argparse object
        print_n : {None}
          function for printing normal message
        print_w : {None}
          function for printing emphized warning message

        Attributes
        ----------
        compiler : Compiler object
          compiler used
        src_dir = {"./"}
          directory into which search source files
        build_dir : {"./"}
          directory containing built files
        obj_dir : {"./"}
          directory containing compiled object files
        mod_dir : {"./"}
          directory containing .mod files
        lib_dir : {None}
          list of directories searched for libraries
        dinc : {None}
          list of directories for searching included files
        libs : {None}
          list of external libraries that are not into the path: must be passed with full paths
        vlibs : {None}
          list of external libraries that are not into the path and that are volatile (can changed thus triggering re-building): must be passed with full paths
        ext_libs : {None}
          list of external libraries that are into the path: are linked as '-llibrary.[a|so]' and searched into '-Ldir'
        ext_vlibs : {None}
          list of external libraries that are into the path and that are volatile (can changed thus triggering re-building): are linked as '-llibrary.[a|so]' and searched into '-Ldir'
        preprocessor : {None}
          activate preprocessor
        preprocessor_dir : {None}
          directory containing sources processed by preprocessor (if any); by default are removed after used
        preprocessor_ext : {None}
          list of file extensions to be processed by preprocessor; by default all files are preprocessed if preprocessor is used
        print_n : {None}
          function for printing normal message
        print_w : {None}
          function for printing emphized warning message
        jobs : {1}
          concurrent compiling jobs
        """

        if print_n is None:
            self.print_n = print_fake
        else:
            self.print_n = print_n

        if print_w is None:
            self.print_w = print_fake
        else:
            self.print_w = print_w

        self.compiler = Compiler(cliargs=cliargs, print_w=self.print_w)

        self.jobs = cliargs.jobs
        self._sanitize_dirs(
            src_dir=cliargs.src,
            build_dir=cliargs.build_dir,
            obj_dir=cliargs.obj_dir,
            mod_dir=cliargs.mod_dir,
            lib_dir=cliargs.lib_dir,
            dinc=cliargs.include,
        )
        self._sanitize_files(
            libs=cliargs.libs, vlibs=cliargs.vlibs, ext_libs=cliargs.ext_libs, ext_vlibs=cliargs.ext_vlibs
        )
        self._set_preprocessor(
            preprocessor=cliargs.preprocessor,
            preprocessor_dir=cliargs.preprocessor_dir,
            preprocessor_ext=cliargs.preprocessor_ext,
            preprocessor_no_o=cliargs.preprocessor_no_o,
            preprocessor_args=cliargs.preprocessor_args,
        )

        self.ar = cliargs.ar
        self.arflags = cliargs.arflags
        self.ranlib = cliargs.ranlib

        self.cmd_comp = self.compiler.compile_cmd(mod_dir=self.mod_dir)
        self.cmd_link = self.compiler.link_cmd(mod_dir=self.mod_dir)
        return

    @staticmethod
    def get_fc(cliargs):
        """
        Method for getting the compiler command built accordingly to the cli arguments.

        Parameters
        ----------
        cliargs : argparse object
        """
        return Compiler(cliargs=cliargs).fcs

    @staticmethod
    def get_cflags(cliargs):
        """
        Method for getting the compiling flags built accordingly to the cli arguments.

        Parameters
        ----------
        cliargs : argparse object
        """
        return Compiler(cliargs=cliargs).cflags

    @staticmethod
    def get_lflags(cliargs):
        """
        Method for getting the linking flags built accordingly to the cli arguments.

        Parameters
        ----------
        cliargs : argparse object
        """
        return Compiler(cliargs=cliargs).lflags

    @staticmethod
    def get_modsw(cliargs):
        """
        Method for getting the compiler modules switch built accordingly to the cli arguments.

        Parameters
        ----------
        cliargs : argparse object
        """
        # modsw = cliargs.modsw
        # if cliargs.compiler.lower() in Builder.s_compilers and modsw == '':
        #   modsw = Builder.s_compilers[cliargs.compiler.lower()][2]
        # return modsw
        return Compiler(compiler=cliargs.compiler, modsw=cliargs.modsw).modsw

    def _sanitize_dirs(self, src_dir, build_dir, obj_dir, mod_dir, lib_dir, dinc):
        """
        Method for sanitizing directory paths.

        Parameters
        ----------
        src_dir : list
          directory into which search source files
        build_dir : str
          directory containing built files
        obj_dir : str
          directory containing compiled object files
        mod_dir : str
          directory containing .mod files
        lib_dir : list
          list of directories searched for libraries
        dinc : list
          list of directories for searching included files
        """
        self.src_dir = " ".join(src_dir)

        self.build_dir = os.path.normpath(build_dir)
        safe_mkdir(directory=self.build_dir, print_w=self.print_w)

        self.obj_dir = os.path.normpath(os.path.join(build_dir, obj_dir))
        safe_mkdir(directory=self.obj_dir, print_w=self.print_w)

        self.mod_dir = os.path.normpath(os.path.join(build_dir, mod_dir))
        safe_mkdir(directory=self.mod_dir, print_w=self.print_w)

        if lib_dir is None:
            self.lib_dir = []
        else:
            self.lib_dir = lib_dir
            for directory in self.lib_dir:
                directory = os.path.normpath(directory)
        if dinc is None:
            self.dinc = []
        else:
            self.dinc = dinc
            for directory in self.dinc:
                directory = os.path.normpath(directory)
        return

    def _sanitize_files(self, libs, vlibs, ext_libs, ext_vlibs):
        """
        Method for sanitizing files paths.

        Parameters
        ----------
        libs : list
          list of external libraries that are not into the path: must be passed with full paths
        vlibs : list
          list of external libraries that are not into the path and that are volatile (can changed thus triggering re-building): must be passed with full paths
        ext_libs : list
          list of external libraries that are into the path: are linked as '-llibrary.[a|so]' and searched into '-Ldir'
        ext_vlibs : list
          list of external libraries that are into the path and that are volatile (can changed thus triggering re-building): are linked as '-llibrary.[a|so]' and searched into '-Ldir'
        """
        if libs is None:
            self.libs = []
        else:
            self.libs = libs
            for lib in self.libs:
                lib = os.path.normpath(lib)
        if vlibs is None:
            self.vlibs = []
        else:
            self.vlibs = vlibs
            for lib in self.vlibs:
                lib = os.path.normpath(lib)
        if ext_libs is None:
            self.ext_libs = []
        else:
            self.ext_libs = ext_libs
            for lib in self.ext_libs:
                lib = os.path.normpath(lib)
        if ext_vlibs is None:
            self.ext_vlibs = []
        else:
            self.ext_vlibs = ext_vlibs
            for lib in self.ext_vlibs:
                lib = os.path.normpath(lib)
        return

    def _set_preprocessor(self, preprocessor, preprocessor_dir, preprocessor_ext, preprocessor_no_o, preprocessor_args):
        """
        Set preprocessor data.

        Parameters
        ----------
        preprocessor : bool
          activate preprocessor
        preprocessor_dir : str
          directory containing sources processed by preprocessor; by default are removed after used
        preprocessor_ext : list
          list of file extensions to be processed by preprocessor; by default all files are preprocessed if preprocessor is used
        """
        self.preprocessor = preprocessor
        if preprocessor_ext is None:
            self.preprocessor_ext = []
        else:
            self.preprocessor_ext = preprocessor_ext
        self.preprocessor_dir = preprocessor_dir
        if self.preprocessor_dir:
            self.preprocessor_dir = os.path.normpath(os.path.join(self.build_dir, self.preprocessor_dir))
            if not os.path.exists(self.preprocessor_dir):
                os.makedirs(self.preprocessor_dir)
        self.preprocessor_no_o = preprocessor_no_o
        self.preprocessor_args = preprocessor_args
        if self.preprocessor:
            preprocessor_exist = False
            for path in os.environ["PATH"].split(os.pathsep):
                preprocessor_exist = os.path.exists(os.path.join(path, self.preprocessor))
                if preprocessor_exist:
                    break
            if not preprocessor_exist:
                self.print_w(
                    "Error: " + self.preprocessor + " is not in your PATH! You cannot use -preprocessor switch."
                )
        return

    def _compile_preprocessor(self, file_to_compile):
        """
        Create compile command with preprocessor usage.

        Parameters
        ----------
        file_to_compile : ParsedFile object
          file to be compiled

        Returns
        -------
        str
          string containing the preprocessor command
        str
          string containing the output file name of preprocessor
        str
          string containing the command for removing/storing preprocessor outputs
        """
        preprocessor_cmd = ""
        preprocessor_output = ""
        preprocessor_remove = ""
        to_preprocess = False
        if self.preprocessor:
            if len(self.preprocessor_ext) > 0:
                if file_to_compile.extension in self.preprocessor_ext:
                    to_preprocess = True
            else:
                to_preprocess = True
            if to_preprocess and self.preprocessor_dir:
                preprocessor_dir = self.preprocessor_dir
                preprocessor_store = True
            else:
                preprocessor_dir = ""
                preprocessor_store = False
            if to_preprocess:
                output_prepend = " -o "
                if self.preprocessor_no_o:
                    output_prepend = " "
                preprocessor_cmd = (
                    self.preprocessor
                    + " "
                    + self.preprocessor_args
                    + " "
                    + file_to_compile.name
                    + output_prepend
                    + os.path.join(preprocessor_dir, file_to_compile.basename + ".pp.f90")
                    + " ; "
                )
                preprocessor_output = os.path.join(preprocessor_dir, file_to_compile.basename + ".pp.f90")
                if not preprocessor_store:
                    preprocessor_remove = " ; rm -f " + preprocessor_output
        return preprocessor_cmd, preprocessor_output, preprocessor_remove

    def _compile_include(self):
        """
        Method for creating compile command with inluded paths.

        Returns
        -------
        str
          string containing the include command
        """
        include_cmd = ""
        if len(self.dinc) > 0:
            include_cmd = "".join(["-I" + s + " " for s in self.dinc])
        return include_cmd

    def _compile_command(self, file_to_compile):
        """
        Method for returning the OS command for compiling file_to_compile.

        Parameters
        ----------
        file_to_compile : ParsedFile object
          file to be compiled

        Returns
        -------
        str
          string containing the compile command
        """
        preprocessor_cmd, preprocessor_output, preprocessor_remove = self._compile_preprocessor(file_to_compile)
        include_cmd = self._compile_include()

        if preprocessor_cmd != "":
            comp_cmd = (
                preprocessor_cmd
                + self.cmd_comp
                + " "
                + include_cmd
                + preprocessor_output
                + " -o "
                + os.path.join(self.obj_dir, file_to_compile.basename + ".o")
                + preprocessor_remove
            )
        else:
            comp_cmd = (
                self.cmd_comp
                + " "
                + include_cmd
                + file_to_compile.name
                + " -o "
                + os.path.join(self.obj_dir, file_to_compile.basename + ".o")
            )
        return comp_cmd

    def _link_command(self, file_to_build, output=None, nomodlibs=None, submodules=None, mklib=None):
        """
        Return the OS command for linkng file_to_build.

        Parameters
        ----------
        file_to_build : ParsedFile object
          file to be built
        output : str
          output name
        nomodlibs : {None}
          list of old-Fortran style libraries objects
        submodules : {None}
          list of submodules objects
        mklib : {None}
          activate building library mode

        Returns
        -------
        str
          string containing the link command
        """
        exe = self.get_output_name(file_to_build=file_to_build, output=output)
        link_cmd = (
            self.cmd_link
            + " "
            + self._get_libs_link_command(
                file_to_build=file_to_build, nomodlibs=nomodlibs, submodules=submodules, mklib=mklib
            )
            + " -o "
            + exe
        )
        return link_cmd, exe

    def _mklib_command(self, file_to_build, output=None, nomodlibs=None, submodules=None, mklib=None):
        """
        Return the OS command for linkng file_to_build as a library.

        Parameters
        ----------
        file_to_build : ParsedFile object
          file to be built
        output : str
          output name
        nomodlibs : {None}
          list of old-Fortran style libraries objects
        submodules : {None}
          list of submodules objects
        mklib : {None}
          activate building library mode

        Returns
        -------
        str
          string containing the link command
        """
        lib = self.get_output_name(file_to_build=file_to_build, output=output, mklib=mklib)
        link_cmd = self._get_libs_link_command(
            file_to_build=file_to_build, exclude_programs=True, nomodlibs=nomodlibs, submodules=submodules, mklib=mklib
        )
        if mklib is not None:
            if mklib.lower() == "shared":
                link_cmd = self.cmd_link + " " + link_cmd + " -o " + lib
            elif mklib.lower() == "static":
                link_cmd = self.ar + " " + self.arflags + " " + lib + " " + link_cmd
                if self.ranlib:
                    link_cmd += " \n " + self.ranlib + " " + lib
        return link_cmd, lib

    def _get_libs_link_command(
        self, file_to_build, exclude_programs=False, nomodlibs=None, submodules=None, mklib=None
    ):
        """
        Return the libraries link command

        Parameters
        ----------
        file_to_build : ParsedFile object
          file to be built
        exclude_programs : {False}
          flag for excluding programs obj from the list
        nomodlibs : {None}
          list of old-Fortran style libraries objects
        submodules : {None}
          list of submodules objects
        mklib : {None}
          activate building library mode

        Returns
        -------
        str
          string containing the link command
        """
        objs = []
        if nomodlibs is not None:
            objs = objs + nomodlibs
        if submodules is not None:
            objs = objs + submodules
        objs = objs + file_to_build.obj_dependencies(exclude_programs=exclude_programs)
        objs = list(set(objs))  # remove duplicated objs
        link_cmd = (
            "".join([os.path.join(self.obj_dir, s + " ") for s in objs])
            + "".join([s + " " for s in self.libs])
            + "".join([s + " " for s in self.vlibs])
        )
        if mklib is None or mklib.lower() == "shared":
            if len(self.ext_libs) > 0:
                link_cmd += " " + "".join(["-l" + s + " " for s in self.ext_libs])
            if len(self.ext_vlibs) > 0:
                link_cmd += " " + "".join(["-l" + s + " " for s in self.ext_vlibs])
            if len(self.lib_dir) > 0:
                link_cmd += " " + "".join(["-L" + s + " " for s in self.lib_dir])
        return link_cmd

    def _get_hierarchy(self, file_to_build):
        """
        Create a hierarchy of compiling commands accordingly to the dependencies order.

        Parameters
        ----------
        file_to_build : ParsedFile
        """
        order_max = (
            max(
                [p for p in file_to_build.pfile_dep_all if not p.include and p.to_compile],
                key=operator.attrgetter("order"),
            ).order
            + 1
        )
        hierarchy = [[] for _ in range(order_max)]
        for dep in file_to_build.pfile_dep_all:
            if dep.to_compile and not dep.include:
                hierarchy[dep.order].append([dep.name, self._compile_command(file_to_compile=dep)])
                dep.to_compile = False
        hierarchy = [h for h in hierarchy if len(h) > 0]
        return hierarchy

    def _compile(self, file_to_build, verbose=False, log=False, quiet=False):
        """
        Compile file.

        Parameters
        ----------
        file_to_build : ParsedFile
        verbose : {False}
          bool for activate extreme verbose outputs
        log : {False}
          bool for activate errors log saving

        Returns
        -------
        build_ok : bool
          flag for checking the building is completed;
          it is used for purging out failed non-module-library in order to try to build the target anywhere;
        """
        build_ok = True
        hierarchy = self._get_hierarchy(file_to_build)
        for deps in reversed(hierarchy):
            files_to_compile = ""
            cmds = []
            for dep in deps:
                files_to_compile = files_to_compile + " " + dep[0]
                cmds.append(dep[1])
            if len(deps) > 1 and self.jobs > 1 and __parallel__:
                jobs = min(len(deps), self.jobs)
                if not quiet:
                    self.print_n("Compiling" + files_to_compile + " using " + str(jobs) + " concurrent processes")
                    if verbose:
                        self.print_n("Executing: " + str(cmds))
                pool = Pool(processes=jobs)
                results = pool.map(syswork, cmds)
                pool.close()
                pool.join()
            else:
                if not quiet:
                    self.print_n("Compiling" + files_to_compile + " serially")
                results = []
                for cmd in cmds:
                    if not quiet:
                        if verbose:
                            self.print_n("Executing: " + str(cmd))
                    result = syswork(cmd)
                    results.append(result)
            if not file_to_build.nomodlib:
                if log:
                    check_results(results=results, log="building-errors.log", print_w=self.print_w)
                else:
                    check_results(results=results, print_w=self.print_w)
        if file_to_build.nomodlib:
            if results[0][0] != 0:
                build_ok = False
                if not quiet:
                    self.print_w(
                        "Warning: compiling file "
                        + file_to_build.name
                        + " fails! Removing from the list of non-module libraries linked into the target!"
                    )
        return build_ok

    def gnu_make(self):
        """
        Return the builder options formated as GNU Make variables

        Returns
        -------
        str
          string containing the builder options in GNU Make format
        """
        string = []
        string.append("DOBJ    = " + os.path.normpath(self.obj_dir) + "/\n")
        string.append("DMOD    = " + os.path.normpath(self.mod_dir) + "/\n")
        string.append("DEXE    = " + os.path.normpath(self.build_dir) + "/\n")
        libs = []
        if len(self.libs) > 0:
            libs += self.libs
        if len(self.vlibs) > 0:
            libs += self.vlibs
        if len(self.ext_libs) > 0:
            libs += ["-l" + l + " " for l in self.ext_libs]
        if len(self.ext_vlibs) > 0:
            libs += ["-l" + l + " " for l in self.ext_vlibs]
        if len(self.lib_dir) > 0:
            libs += ["-L" + l + " " for l in self.lib_dir]
        if len(libs) > 0:
            string.append("LIBS    = " + "".join(" " + l for l in libs) + "\n")
        else:
            string.append("LIBS    =\n")
        string.append("FC      = " + self.compiler.fcs + "\n")
        string.append("OPTSC   = " + self.compiler.cflags + " " + self.compiler.modsw + self.mod_dir + "\n")
        string.append("OPTSL   = " + self.compiler.lflags + " " + self.compiler.modsw + self.mod_dir + "\n")
        return "".join(string)

    def build(
        self,
        file_to_build,
        output=None,
        nomodlibs=None,
        submodules=None,
        mklib=None,
        verbose=False,
        log=False,
        quiet=False,
        track=False,
    ):
        """
        Build a file.

        Parameters
        ----------
        file_to_build : ParsedFile
        output : str
          output name
        nomodlibs : {None}
          list of old-Fortran style libraries objects
        submodules : {None}
          list of submodules objects
        mklib : {None}
          bool for activate building library mode
        verbose : {False}
          bool for activate extreme verbose outputs
        log : {False}
          bool for activate errors log saving
        quiet : {False}
          bool for activate quiet mode
        track : {False}
          bool for activate build traking mode for subsequent install command

        Returns
        -------
        build_ok : bool
          flag for checking the building is completed;
          it is used for purging out failed non-module-library in order to try to build the target anywhere;
        """
        build_ok = True
        if not quiet:
            self.print_n("Building " + file_to_build.name)
        # sort dependencies accordingly to indirect dependency
        file_to_build.sort_dependencies()
        # creating a hierarchy list of compiling commands accordingly to the order of all dependencies
        if len([p for p in file_to_build.pfile_dep_all if not p.include and p.to_compile]) > 0:
            build_ok = self._compile(file_to_build=file_to_build, verbose=verbose, log=log, quiet=quiet)
        else:
            if not quiet:
                self.print_n("Nothing to compile, all objects are up-to-date")
        if file_to_build.program:
            link_cmd, exe = self._link_command(
                file_to_build=file_to_build, output=output, nomodlibs=nomodlibs, submodules=submodules, mklib=mklib
            )
            if not quiet:
                self.print_n("Linking " + exe)
                if verbose:
                    self.print_n("Executing: " + str(link_cmd))
            result = syswork(link_cmd)
            if log:
                check_results(results=[result], log="building-errors.log", print_w=self.print_w)
            else:
                check_results(results=[result], print_w=self.print_w)
            if not quiet:
                self.print_n("Target " + file_to_build.name + " has been successfully built")
        elif mklib:
            link_cmd, lib = self._mklib_command(
                file_to_build=file_to_build, output=output, nomodlibs=nomodlibs, submodules=submodules, mklib=mklib
            )
            if not quiet:
                self.print_n("Linking " + lib)
                if verbose:
                    self.print_n("Executing: " + str(link_cmd))
            result = syswork(link_cmd)
            if log:
                check_results(results=[result], log="building-errors.log", print_w=self.print_w)
            else:
                check_results(results=[result], print_w=self.print_w)
            if not quiet:
                self.print_n("Target " + file_to_build.name + " has been successfully built")
        if track:
            print("Track building of " + file_to_build.name)
            track_file_name = self.get_track_build_file(file_to_build)
            with open(track_file_name, "w") as track_file:
                track_file.writelines("[build]\n")
                track_file.writelines(
                    "output = " + self.get_output_name(file_to_build=file_to_build, output=output) + "\n"
                )
                if file_to_build.program:
                    track_file.writelines("program = True\n")
                elif mklib:
                    track_file.writelines("library = True\n")
                    track_file.writelines(
                        "mod_file = " + os.path.join(self.mod_dir, file_to_build.basename + ".mod") + "\n"
                    )
        return build_ok

    def get_output_name(self, file_to_build, output=None, mklib=None):
        """
        Return the output build file name.

        Parameters
        ----------
        file_to_build : ParsedFile
        output : str
          output build file name
        mklib : {None}
          activate building library mode

        Returns
        -------
        output : str
          output build file name
        """
        if output:
            build_name = os.path.join(self.build_dir, output)
        else:
            if mklib is not None:
                if mklib.lower() == "shared":
                    build_name = os.path.join(self.build_dir, file_to_build.basename + ".so")
                elif mklib.lower() == "static":
                    build_name = os.path.join(self.build_dir, file_to_build.basename + ".a")
            else:
                build_name = os.path.join(self.build_dir, file_to_build.basename)
        return build_name

    def get_track_build_file(self, file_to_build):
        """
        Return the file name of the 'track build' file.

        Parameters
        ----------
        file_to_build : ParsedFile

        Returns
        -------
        track_file_name : str
          file name of 'track build' file
        """
        return os.path.join(self.build_dir, "." + os.path.basename(file_to_build.name) + ".track_build")

    def verbose(self, quiet=False):
        """
        The method verbose returns a verbose message containing builder infos.

        Parameters
        ----------
        quiet : {False}
          flag for making less verbose outputs
        """
        message = ""
        if not quiet:
            message = "Builder options" + "\n"
            message += "  Directories\n"
            message += '    Root of source files directory: "' + self.src_dir + '"\n'
            message += '    Building directory: "' + self.build_dir + '"\n'
            message += '    Compiled-objects .o   directory: "' + self.obj_dir + '"\n'
            message += '    Compiled-objects .mod directory: "' + self.mod_dir + '"\n'
            if len(self.lib_dir) > 0:
                message += "  External libraries directories: " + "".join([s + " " for s in self.lib_dir]) + "\n"
            if len(self.dinc) > 0:
                message += "  Included paths: " + "".join([s + " " for s in self.dinc]) + "\n"
            if len(self.libs) > 0:
                message += "  Linked libraries with full path: " + "".join([s + " " for s in self.libs]) + "\n"
            if len(self.vlibs) > 0:
                message += (
                    "  Linked volatile libraries with full path: " + "".join([s + " " for s in self.vlibs]) + "\n"
                )
            if len(self.ext_libs) > 0:
                message += "  Linked libraries in path: " + "".join([s + " " for s in self.ext_libs]) + "\n"
            if len(self.ext_vlibs) > 0:
                message += "  Linked volatile libraries in path: " + "".join([s + " " for s in self.ext_vlibs]) + "\n"
            message += self.compiler.pprint(prefix="  ")
            message += "  Archiver: " + self.ar + " " + self.arflags + "\n"
            if self.ranlib:
                message += "  Ranlib: " + self.ranlib + "\n"
            message += "  Preprocessor used: " + str(self.preprocessor) + "\n"
            message += "  Preprocessor args: " + str(self.preprocessor_args) + "\n"
            message += "  Preprocessor output directory: " + str(self.preprocessor_dir) + "\n"
            message += "  Preprocessor extensions processed: " + str(self.preprocessor_ext) + "\n"
        return message
