build_exe.py 8.34 KB
Newer Older
1 2 3 4 5 6 7 8
'''
Created on 20 oct. 2012

@author: coissac
'''

import os

9
from distutils import sysconfig
10
from distutils.core import Command
11
from distutils.sysconfig import customize_compiler as customize_compiler_ori
12 13 14 15
from distutils.errors import DistutilsSetupError
from distutils import log
from distutils.ccompiler import show_compilers

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
def customize_compiler(compiler):
    customize_compiler_ori(compiler)
    compilername = compiler.compiler[0]
    if ("gcc" in compilername or "g++" in compilername):
        cc_cmd = ' '.join(compiler.compiler + ['-fopenmp'])
        ccshared= ' '.join(x for x in sysconfig.get_config_vars("ccshared") if x is not None)
            
        compiler.set_executables(
            compiler=cc_cmd,
            compiler_so=cc_cmd + ' ' + ccshared
            )
            
    
    

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
class build_exe(Command):
    
    description = "build an executable -- Abstract command "

    user_options = [
        ('build-cexe', 'x',
         "directory to build C/C++ libraries to"),
        ('build-temp', 't',
         "directory to put temporary build by-products"),
        ('debug', 'g',
         "compile with debugging information"),
        ('force', 'f',
         "forcibly build everything (ignore file timestamps)"),
        ('compiler=', 'c',
         "specify the compiler type"),
        ]

    boolean_options = ['debug', 'force']

    help_options = [
        ('help-compiler', None,
         "list available compilers", show_compilers),
        ]

    def initialize_options(self):
        self.build_cexe = None
        self.build_temp = None

        # List of executables to build
        self.executables = None

        # Compilation options for all libraries
        self.include_dirs = None
        self.define = None
        self.undef = None
        self.extra_compile_args = None
        self.debug = None
        self.force = 0
        self.compiler = None
        self.sse = None
        self.built_files=None

    def finalize_options(self):
        # This might be confusing: both build-cexe and build-temp default
        # to build-temp as defined by the "build" command.  This is because
        # I think that C libraries are really just temporary build
        # by-products, at least from the point of view of building Python
        # extensions -- but I want to keep my options open.
        self.set_undefined_options('build',
                                   ('build_temp', 'build_temp'),
                                   ('compiler', 'compiler'),
                                   ('debug', 'debug'),
                                   ('force', 'force'))

        if self.include_dirs is None:
            self.include_dirs = self.distribution.include_dirs or []
            
        if isinstance(self.include_dirs, str):
            self.include_dirs = self.include_dirs.split(os.pathsep)

        self.sse = self.distribution.sse
        
        if self.sse is not None:
            if self.extra_compile_args is None:
                self.extra_compile_args=['-m%s' % self.sse]
            else:
                self.extra_compile_args.append('-m%s' % self.sse)

99

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
        # XXX same as for build_ext -- what about 'self.define' and
        # 'self.undef' ?

    def run(self):

        if not self.executables:
            return

        self.mkpath(self.build_cexe)

        # Yech -- this is cut 'n pasted from build_ext.py!
        from distutils.ccompiler import new_compiler
        self.compiler = new_compiler(compiler=self.compiler,
                                     dry_run=self.dry_run,
                                     force=self.force)
        customize_compiler(self.compiler)
116
        
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
        if self.include_dirs is not None:
            self.compiler.set_include_dirs(self.include_dirs)
        if self.define is not None:
            # 'define' option is a list of (name,value) tuples
            for (name,value) in self.define:
                self.compiler.define_macro(name, value)

        if self.undef is not None:
            for macro in self.undef:
                self.compiler.undefine_macro(macro)
                
        self.build_executables(self.executables)


    def check_executable_list(self, executables):
        """Ensure that the list of executables is valid.

        `executable` is presumably provided as a command option 'executables'.
        This method checks that it is a list of 2-tuples, where the tuples
        are (executable_name, build_info_dict).

        Raise DistutilsSetupError if the structure is invalid anywhere;
        just returns otherwise.
        """
        if not isinstance(executables, list):
            raise DistutilsSetupError("'executables' option must be a list of tuples")

        for exe in executables:
            if not isinstance(exe, tuple) and len(exe) != 2:
                raise DistutilsSetupError("each element of 'executables' must a 2-tuple")

            name, build_info = exe

            if not isinstance(name, str):
                raise DistutilsSetupError(
                      "first element of each tuple in 'executables' " 
                      "must be a string (the executables name)")
                
            if '/' in name or (os.sep != '/' and os.sep in name):
                raise DistutilsSetupError(
                       "bad executable name '%s': " 
                       "may not contain directory separators" % exe[0])

            if not isinstance(build_info, dict):
                raise DistutilsSetupError(
                      "second element of each tuple in 'executables' " 
                      "must be a dictionary (build info)")

    def get_executable_names(self):
        # Assume the executables list is valid -- 'check_executable_list()' is
        # called from 'finalize_options()', so it should be!
        if not self.executables:
            return None

        exe_names = []
        for (exe_name, build_info) in self.executables:                     # @UnusedVariable
            exe_names.append(exe_name)
        return exe_names


    def get_source_files(self):
        self.check_executable_list(self.executables)
        filenames = []
        for (exe_name, build_info) in self.executables:                     # @UnusedVariable
            sources = build_info.get('sources')
            if sources is None or not isinstance(sources, (list, tuple)):
                raise DistutilsSetupError(
                       "in 'executables' option (library '%s'), "
                       "'sources' must be present and must be "
                       "a list of source filenames" % exe_name)

            filenames.extend(sources)
        return filenames
    
    def substitute_sources(self,exe_name,sources):
        return list(sources)

    def build_executables(self, executables):
        for (exe_name, build_info) in executables:
            sources = build_info.get('sources')
            if sources is None or not isinstance(sources, (list, tuple)):
                raise DistutilsSetupError(
                       "in 'executables' option (library '%s'), " 
                       "'sources' must be present and must be " 
                       "a list of source filenames" % exe_name)
                
            sources = self.substitute_sources(exe_name,sources)

            log.info("building '%s' program", exe_name)

            # First, compile the source code to object files in the library
            # directory.  (This should probably change to putting object
            # files in a temporary build directory.)
            macros = build_info.get('macros')
            include_dirs = build_info.get('include_dirs')
            extra_args = self.extra_compile_args or []
            
            objects = self.compiler.compile(sources,
                                            output_dir=self.build_temp,
                                            macros=macros,
                                            include_dirs=include_dirs,
                                            extra_postargs=extra_args,
                                            debug=self.debug)

            # Now "link" the object files together into a static library.
            # (On Unix at least, this isn't really linking -- it just
            # builds an archive.  Whatever.)
            self.compiler.link_executable(objects, exe_name,
                                            output_dir=self.build_cexe,
                                            debug=self.debug)

228