示例#1
0
 def _resolvePaths(self, absPath, paths):
   i = 0
   for path in paths:
     paths[i] = utils.makePathAbsolute(absPath, path)
     i += 1
示例#2
0
  def __init__(self, argv):
    '''
      argv (input): a list of arguments with a flag, value pairing ie ['-c', 'gcc'] (where -c is the flag and gcc is the value)
    '''

    # get any arguments passed in
    if type(argv) is not list:
      raise Exception('args must be a list, not a {0}'.format(argv))

    self.target = ''
    self.binaryType = ''  # exe, static, dynamic, plugin
    self.compiler = ''  # g++-4.4 g++ clang++ msvc110 etc
    self.osType = ''  # linux, osx, windows
    self.binaryFormat = '64bit'  # 32bit, 64bit etc
    self.buildType = 'debug'  # debug, release etc

    self.hiddenFiles = False  # if pybythec files are "hidden files"

    self.filetype = ''  # elf, mach-o, pe

    self.multithread = True

    self.locked = False

    self.showCompilerCmds = False
    self.showLinkerCmds = False

    self.buildDir = 'pybythec'
    self.hideBuildDirs = False

    self.installPath = '.'  #self.buildDir

    self.configKeys = [] # can be declared in the config files
    self.customKeys = [] # custom keys that are both declared on the command line and found in the config file(s)

    self.sources = []
    self.libs = []
    self.defines = []
    self.flags = []
    self.linkFlags = []

    self.incPaths = []
    self.extIncPaths = []  # these will not be checked for timestamps
    self.libPaths = []
    self.libSrcPaths = []
    self.keys = []

    self.qtClasses = []

    self.libInstallPathAppend = True
    self.plusplus = True

    # defaults
    if platform.system() == 'Linux':
      if not len(self.osType):
        self.osType = 'linux'
      if not len(self.compiler):
        self.compiler = 'g++'
      if not len(self.filetype):
        self.filetype = 'elf'
    elif platform.system() == 'Darwin':
      if not len(self.osType):
        self.osType = 'osx'
      if not len(self.compiler):
        self.compiler = 'clang++'
      if not len(self.filetype):
        self.filetype = 'mach-o'
    elif platform.system() == 'Windows':
      if not len(self.osType):
        self.osType = 'windows'
      if not len(self.compiler):
        i = 25  # NOTE: hopefully that covers enough VisualStudio releases
        vcPath = 'C:/Program Files (x86)/Microsoft Visual Studio {0}.0/VC'
        foundVc = False
        while i > 5:
          if os.path.exists(vcPath.format(i)):
            foundVc = True
            break
          i -= 1
        if foundVc:
          self.compiler = 'msvc-{0:02}0'.format(i)
        else:
          raise Exception('can\'t find a compiler for Windows')
      if not len(self.filetype):
        self.filetype = 'pe'
    else:
      raise Exception('os does not appear to be Linux, OS X or Windows')

    #
    # parse the args
    #
    args = dict()
    argKey = str()
    keyFound = False

    for arg in argv:
      if keyFound:
        args[argKey] = arg
        keyFound = False
        continue
      if arg == '-cl' or arg == '-cla':  # cleaning
        args[arg] = ''
      elif arg == '-c' or arg == '-os' or arg == '-b' or arg == '-bf' or arg == '-d' or arg == '-p' or arg == '-ck':
        argKey = arg
        keyFound = True
      elif arg == '-v':
        raise Exception('version: {0}'.format(utils.__version__))
      else:
        raise Exception('{0} is not a valid argumnet\nvalid arguments:\n\n'
                        '-c   compiler: any variation of gcc, clang, or msvc ie g++-4.4, msvc110\n'
                        '-os  operating system: currently linux, osx, or windows\n'
                        '-b   build type: debug release etc \n'
                        '-bf  binary format: 32bit, 64bit etc\n'
                        '-p   path to a pybythec project config file (json format)\n'
                        '-cl  clean the build\n'
                        '-cla clean the build and the builds of all library dependencies\n'
                        '-v   version\n'
                        '-ck  custom keys that you want this build to use (comma delineated, no spaces ie foo,bar)\n'
                        '-d   directory of the library being built, likely only used when building a library as a dependency (ie from a project)\n'.format(arg))

    self.cwDir = os.getcwd()
    if '-d' in args:
      self.cwDir = args['-d']

  # json config files
    globalCf = None
    projectCf = None
    localCf = None

    # global config
    if not globalCf and '-g' in args:
      globalCf = utils.loadJsonFile(args['-g'])
    if 'PYBYTHEC_GLOBALS' in os.environ:
      globalCf = utils.loadJsonFile(os.environ['PYBYTHEC_GLOBALS'])
    if not globalCf:
      globalCf = utils.loadJsonFile('.pybythecGlobals.json')
    if not globalCf:
      globalCf = utils.loadJsonFile('pybythecGlobals.json')  
    if not globalCf:
      homeDirPath = ''
      if platform.system() == 'Windows':
        homeDirPath = os.environ['USERPROFILE']
      else:
        homeDirPath = os.environ['HOME']
      globalCf = utils.loadJsonFile(homeDirPath + '/.pybythecGlobals.json')
      if not globalCf:
        globalCf = utils.loadJsonFile(homeDirPath + '/pybythecGlobals.json')
    if not globalCf:
      log.warning('no global pybythec json file found')

  # project config
    if 'PYBYTHEC_PROJECT' in os.environ:
      projectCf = os.environ['PYBYTHEC_PROJECT']
    if not projectCf and '-p' in args:
      projectCf = utils.loadJsonFile(args['-p'])
    if not projectCf:
      projectCf = utils.loadJsonFile(self.cwDir + '/pybythecProject.json')
    if not projectCf:
      projectCf = utils.loadJsonFile(self.cwDir + '/.pybythecProject.json')

  # local config, expected to be in the current working directory
    localConfigPath = self.cwDir + '/pybythec.json'
    if not os.path.exists(localConfigPath):
      localConfigPath = self.cwDir + '/.pybythec.json'
    if os.path.exists(localConfigPath):
      localCf = utils.loadJsonFile(localConfigPath)

    if globalCf is not None:
      self._getBuildElements(globalCf)
    if projectCf is not None:
      self._getBuildElements(projectCf)
    if localCf is not None:
      self._getBuildElements(localCf)

    # command line overrides
    if '-os' in args:
      self.osType = args['-os']

    if '-b' in args:
      self.buildType = args['-b']

    if '-bf' in args:
      self.binaryFormat = args['-bf']

    # compiler special case: os specific compiler selection
    if type(self.compiler) == dict:
      compiler = []
      self.keys = [self.osType]
      if globalCf and 'compiler' in globalCf:
        self._getArgsList(compiler, globalCf['compiler'])
      if projectCf and 'compiler' in projectCf:
        self._getArgsList(compiler, projectCf['compiler'])
      if localCf and 'compiler' in localCf:
        self._getArgsList(compiler, localCf['compiler'])
      if len(compiler):
        self.compiler = compiler[0]

    # one final commandline override: the compiler
    if '-c' in args:
      self.compiler = args['-c']

    # TODO: verify things like does this compiler actually exist (to prevent getting poor error messages)

    # currently compiler root can either be gcc, clang or msvc
    self.compilerRoot = self.compiler
    if self.compilerRoot.startswith('gcc') or self.compilerRoot.startswith('g++'):
      self.compilerRoot = 'gcc'
    elif self.compilerRoot.startswith('clang') or self.compilerRoot.startswith('clang++'):
      self.compilerRoot = 'clang'
    elif self.compilerRoot.startswith('msvc'):
      self.compilerRoot = 'msvc'
    else:
      raise Exception('unrecognized compiler {0}'.format(self.compiler))

    # compiler version
    self.compilerVersion = ''
    v = self.compiler.split('-')
    if len(v) > 1:
      self.compilerVersion = '-' + v[1]

    self.keys = ['all', self.compilerRoot, self.compiler, self.osType, self.binaryType, self.buildType, self.binaryFormat]

    # custom keys
    if '-ck' in args:
      cmdLineKeys = args['-ck']
      for ck in self.configKeys:
        if type(cmdLineKeys) == str:
          if ck == cmdLineKeys:
            self.customKeys.append(ck)
        else: # assume list
          if ck in cmdLineKeys:
            self.customKeys.append(ck)
      self.keys += self.customKeys

    if self.multithread:
      self.keys.append('multithread')

    if globalCf is not None:
      self._getBuildElements2(globalCf)
    if projectCf is not None:
      self._getBuildElements2(projectCf)
    if localCf is not None:
      self._getBuildElements2(localCf)

    # deal breakers
    if not len(self.target):
      raise Exception('no target specified')
    elif not len(self.binaryType):
      raise Exception('no binary type specified')
    elif not len(self.binaryFormat):
      raise Exception('no binary format specified')
    elif not len(self.buildType):
      raise Exception('no build type specified')
    elif not len(self.sources):
      raise Exception('no source files specified')

    if not (self.binaryType == 'exe' or self.binaryType == 'static' or self.binaryType == 'dynamic' or self.binaryType == 'plugin'):
      raise Exception('unrecognized binary type: ' + self.binaryType)

    if self.hideBuildDirs:
      self.buildDir = '.' + self.buildDir

    #
    # compiler config
    #
    self.compilerCmd = self.compiler
    self.linker = ''
    self.targetFlag = ''
    self.libFlag = ''
    self.libPathFlag = ''
    self.objExt = ''
    self.objPathFlag = ''

    self.staticExt = ''
    self.dynamicExt = ''
    self.pluginExt = ''

    #
    # gcc / clang
    #
    if self.compilerRoot == 'gcc' or self.compilerRoot == 'clang':

      if not self.plusplus:  # if forcing plain old C (ie when a library is being built as a dependency that is only C compatible)
        if self.compilerRoot == 'gcc':
          self.compilerCmd = self.compilerCmd.replace('g++', 'gcc')
        elif self.compilerRoot == 'clang':
          self.compilerCmd = self.compilerCmd.replace('clang++', 'clang')

      self.objFlag = '-c'
      self.objExt = '.o'
      self.objPathFlag = '-o'
      self.defines.append('_' + self.binaryFormat.upper())  # TODO: you sure this is universal?

      # link
      self.linker = self.compilerCmd  # 'ld'
      self.targetFlag = '-o'
      self.libFlag = '-l'
      self.libPathFlag = '-L'
      self.staticExt = '.a'
      self.dynamicExt = '.so'
      self.pluginExt = '.so'

      # log.info('*** filetype {0}'.format(self.filetype))

      if self.filetype == 'mach-o':
        self.dynamicExt = '.dylib'
        self.pluginExt = '.bundle'

      if self.binaryType == 'static' or self.binaryType == 'dynamic':
        self.target = 'lib' + self.target

      if self.binaryType == 'exe':
        pass
      elif self.binaryType == 'static':
        self.target = self.target + '.a'
        self.linker = 'ar'
        self.targetFlag = 'r'
      elif self.binaryType == 'dynamic':
        self.target = self.target + self.dynamicExt
      elif self.binaryType == 'plugin':
        self.target = self.target + self.pluginExt

    #
    # msvc / msvc
    #
    elif self.compilerRoot == 'msvc':

      # compile
      self.compilerCmd = 'cl'
      self.objFlag = '/c'
      self.objExt = '.obj'
      self.objPathFlag = '/Fo'

      # link
      self.linker = 'link'
      self.targetFlag = '/OUT:'
      self.libFlag = ''
      self.libPathFlag = '/LIBPATH:'
      self.staticExt = '.lib'
      self.dynamicExt = '.dll'
      if self.binaryFormat == '64bit':
        self.linkFlags.append('/MACHINE:X64')

      if self.binaryType == 'exe':
        self.target += '.exe'
      elif self.binaryType == 'static':
        self.target += self.staticExt
        self.linker = 'lib'
      elif self.binaryType == 'dynamic' or self.binaryType == 'plugin':
        self.target += self.dynamicExt
        self.linkFlags.append('/DLL')

      # make sure the compiler is in PATH
      if utils.runCmd(self.compilerCmd).startswith('[WinError 2]'):
        raise Exception('compiler not found, check the paths set in bins')

    else:
      raise Exception('unrecognized compiler root: ' + self.compilerRoot)

    #
    # determine paths
    #
    self.installPath = utils.makePathAbsolute(self.cwDir, self.installPath)
    self._resolvePaths(self.cwDir, self.sources)
    self._resolvePaths(self.cwDir, self.incPaths)
    self._resolvePaths(self.cwDir, self.extIncPaths)
    self._resolvePaths(self.cwDir, self.libPaths)
    self._resolvePaths(self.cwDir, self.libSrcPaths)

    self.binaryRelPath = '/{0}/{1}/{2}'.format(self.buildType, self.compiler, self.binaryFormat)

    binRelPath = self.binaryRelPath 
    for ck in self.customKeys:
      binRelPath += '/' + ck

    self.buildPath = utils.makePathAbsolute(self.cwDir, './' + self.buildDir + binRelPath)

    if self.libInstallPathAppend and (self.binaryType == 'static' or self.binaryType == 'dynamic'):
      self.installPath += binRelPath

    self.targetInstallPath = os.path.join(self.installPath, self.target)

    self.infoStr = '{0} ({1} {2} {3}'.format(self.target, self.buildType, self.compiler, self.binaryFormat)
    if len(self.customKeys):
      for ck in self.customKeys:
        self.infoStr += ' ' + ck
    self.infoStr += ')'