示例#1
0
文件: clean.py 项目: seyhajin/flux
def run(flux_dir, proj_dir, args):
    """clean intermediate project dirs"""
    if len(args) > 0:
        print(args)

        # clean opts
        opts = BuildOpts() #TODO: CleanOpts or global Opts?
        args = opts.parse_opts(proj_dir, args)

        # target
        #target = Target(flux_dir, opts)

        for arg in args:
            arg = util.fix_path(arg)
            path = os.path.join(proj_dir, arg)

            proj = Project(flux_dir, path, opts)

            # change to project dir
            #os.chdir(os.path.abspath(path))

            # clean output dir
            if opts.verbose >= 1:
                log.info("cleaning `%s`: `%s`" % (proj.profile, proj.out_dir))
            proj.clean()
    else:
        # show usage
        usage()
示例#2
0
文件: build.py 项目: seyhajin/flux
    def parse_opts(self, proj_dir, args):
        '''parse build options'''
        self.args = args

        # parse arguments
        i = 0
        for arg in args:
            # options without params
            if arg in ['-v', '-verbose']:
                self.verbose = 1
            elif arg in ['-t', '-time']:
                self.time = True
            elif arg in ['-c', '-clean']:
                self.clean = True
            else:
                # options with params
                if arg.startswith('-'):
                    i = arg.find('=')
                    if i == -1:
                        log.fatal('expected value for option `%s`' % arg)

                    opt, _, val = arg.partition('=')
                    path = util.fix_path(val)
                    if path.startswith('"') and path.endswith('"'):
                        path = path[1:-1]

                    opt = opt.lower()
                    val = val.lower()

                    # outdir (output dir)
                    if opt in ['-o', '-outdir']:
                        # relative to current dir
                        self.outdir = os.path.abspath(
                            os.path.join(proj_dir, path))
                    # apptype
                    elif opt == '-apptype':
                        if val in APP_TYPE:
                            self.apptype = val
                        else:
                            log.fatal(
                                'invalid value for `apptype` option: `{}` - must be {}'
                                .format(val, APP_TYPE))
                    # target
                    elif opt == '-target':
                        if val in target.TARGET or val == 'desktop':
                            self.target = val
                        else:
                            log.fatal(
                                'invalid value for `target` option: `{}` - must be {} or `desktop`'
                                .format(val, target.TARGET))
                    # config
                    elif opt == '-config':
                        if val in target.CONFIG:
                            self.config = val
                        elif val == 'all':
                            self.config = ','.join(target.CONFIG)
                        else:
                            log.fatal(
                                'invalid value for `config` option: `{}` - must be {} or `all`'
                                .format(val, target.CONFIG))
                    # arch
                    elif opt == '-arch':
                        if val in target.ARCH:
                            self.arch = val
                        else:
                            log.fatal(
                                'invalid value for `arch` option: `{}` - must be {}'
                                .format(val, target.ARCH))
                    # verbose
                    elif opt in ['-v', '-verbose']:
                        if val in ['0', '1', '2', '3', '-1']:
                            self.verbose = int(val)
                        else:
                            log.fatal(
                                'invalid value for `verbose` option: `{}` - must be {}'
                                .format(val, "['0', '1', '2', '3' or '-1']"))
                    # profile
                    elif opt == '-profile':
                        #TODO: -profile=windows-msvc-release-x64; macos-release-x64 ; ios-sim-release-arm64 ; emscripten-release-wasm
                        log.todo(
                            'set profile config: <target>-<config>-<arch>[-<tag>]',
                            __file__)
                        pass
                    # tag
                    elif opt == '-tag':
                        self.tag = val
                    else:
                        log.fatal('unrecognized option: `%s`' % arg)
                else:
                    #paths of project file
                    i = args.index(arg)
                    break

        # get project paths
        args = args[i:]

        # adjust build options
        if self.target == '' or self.target == 'desktop':
            self.target = util.get_host_platform()

        self.toolchain = 'gcc'
        if self.target == 'windows-msvc':
            self.toolchain = 'msvc'

        if self.target in target.TARGET_DESKTOP:
            if self.apptype == '':
                self.apptype = APP_TYPE[0]
        elif self.target in target.TARGET and self.target not in target.TARGET_DESKTOP:
            if self.target == 'emscripten':
                if not self.apptype:
                    self.apptype = 'wasm'
                    self.arch = 'wasm'
            else:
                log.todo(
                    "need to ajust other target options (%s)" % self.target,
                    __file__)
        else:
            log.fatal('unrecognized target `%s` or not yet implemented' %
                      self.target)

        if self.apptype in APP_TYPE:
            if self.target not in target.TARGET_DESKTOP:
                log.fatal('`apptype` `%s` is only valid for desktop targets' %
                          self.apptype)
        elif self.apptype == 'wasm':
            if self.target not in target.TARGET_WEB:
                log.fatal(
                    '`apptype` `%s` is only valid for emscripten target' %
                    self.apptype)
        else:
            log.fatal('unrecognized apptype `%s`' % self.apptype)

        # set built profile: <target>-<config>-<arch>[-<tag>]
        self.profile = '-'.join([self.target, self.config, self.arch]) + (
            ('-' + self.tag) if self.tag else '')

        return args
示例#3
0
    def __init__(self, flux_dir, proj_dir, build_opts, is_dep=False):
        '''load project file and prepare intermediate dirs'''

        # project yaml datas
        self.data = None

        # init vars
        self.imported = {}

        # project inputs
        self.src_files = []
        self.lib_files = []
        self.obj_files = []
        self.bin_files = []
        self.java_files = []
        self.asset_files = []
        self.ninja_files = []

        # flux project
        self.flux_file = ''
        self.flux_srcs = []
        self.flux_libs = []

        # project build opts
        self.cc_opts = []
        self.cxx_opts = []
        self.ar_opts = []
        self.as_opts = []
        self.ld_opts = []

        self.include_dirs = []
        self.library_dirs = []

        # load project file datas
        proj_dir = util.fix_path(proj_dir)
        proj_file = ''
        if os.path.isdir(proj_dir):
            #log.track('is_dir: '+util.strip_dir(proj_dir), __file__)
            proj_files = [
                os.path.join(proj_dir, FILE),  # flux.yml
                os.path.join(proj_dir,
                             util.strip_dir(proj_dir) +
                             '.yml'),  # <dirname>.yml
                os.path.join(proj_dir,
                             util.strip_dir(proj_dir) +
                             '.flux'),  # <dirname>.flux
            ]
            for file in proj_files:
                #log.track('proj_file: '+file, __file__)
                if os.path.isfile(file):
                    proj_file = file
                    self.data = util.load_flux_yaml(proj_file, build_opts)
                    self.flux_file = proj_file
                    break

        elif os.path.isfile(proj_dir):
            #log.track('is_file: '+file, __file__)
            proj_file = proj_dir
            proj_dir = util.split_dir(proj_dir)
            self.data = util.load_flux_yaml(proj_file, build_opts)
            self.flux_file = proj_file

        if not self.data:
            log.fatal('unable to find project file in `%s`' % proj_dir)

        # get project name in project file else name will be the name of last folder
        self.name = self.data['name'] if self.data.get(
            'name') else util.strip_dir(proj_dir)

        # update build opts
        build_opts.build = self.data['build'] if self.data.get(
            'build') else 'app'
        build_opts.apptype = self.data['type'] if self.data.get(
            'type') else 'window'
        build_opts.build = build.remap(build_opts.build)
        build_opts.target = build.remap(build_opts.target)

        # keep build options for project
        self.build = build_opts.build
        self.apptype = build_opts.apptype if build_opts.build == 'app' else ''  # only for app
        self.profile = build_opts.profile
        self.target = build_opts.target
        self.toolchain = build_opts.toolchain

        # set project directory structure
        if build_opts.outdir and not is_dep:
            self.proj_dir = proj_dir
            self.base_dir = util.fix_path(build_opts.outdir)
            self.build_dir = util.fix_path(
                os.path.join(self.base_dir, self.name))
            self.out_dir = util.fix_path(
                os.path.join(self.build_dir, build_opts.profile))
            self.cache_dir = util.fix_path(
                os.path.join(self.out_dir, CACHE_DIR))
            self.asset_dir = util.fix_path(
                os.path.join(self.out_dir, ASSET_DIR))
        else:
            self.proj_dir = proj_dir
            self.base_dir = util.fix_path(proj_dir)
            self.build_dir = util.fix_path(os.path.join(self.base_dir, FDIR))
            self.out_dir = util.fix_path(
                os.path.join(self.build_dir, build_opts.profile))
            self.cache_dir = util.fix_path(
                os.path.join(self.out_dir, CACHE_DIR))
            self.asset_dir = util.fix_path(
                os.path.join(self.out_dir, ASSET_DIR))

        # get output extension
        self.out_ext = build.EXT[build_opts.target][build_opts.build]
        if (self.target, self.build, self.apptype) == ('macos', 'app',
                                                       'console'):
            self.out_ext = ''  # macos console app

        #if (self.target, self.build) == ('emscripten', 'mod'): # fix emscripten 2.0.17
        #    self.out_file = util.fix_path(os.path.join(self.out_dir, 'lib' + self.name + self.out_ext))
        #else:
        #    self.out_file = util.fix_path(os.path.join(self.out_dir, self.name + self.out_ext))
        self.out_file = util.fix_path(
            os.path.join(self.out_dir, self.name + self.out_ext))

        # set gen file
        self.gen_file = util.fix_path(os.path.join(self.out_dir, NINJA_FILE))

        # get build options
        if 'options' in self.data:
            self.opts = self.data['options'] if self.data.get(
                'options') else {}
            self.cc_opts.append(self.data['options']['cc'] if self.
                                data['options'].get('cc') else '')
            self.cxx_opts.append(self.data['options']['cxx'] if self.
                                 data['options'].get('cxx') else '')
            self.as_opts.append(self.data['options']['as'] if self.
                                data['options'].get('as') else '')
            self.ar_opts.append(self.data['options']['ar'] if self.
                                data['options'].get('ar') else '')
            self.ld_opts.append(self.data['options']['ld'] if self.
                                data['options'].get('ld') else '')

        # add build options for app
        if self.build in ['app', 'application']:
            # add workspace dir in header-dirs
            self.cc_opts.append('-I"%s"' % util.get_workspace_dir(flux_dir))
            self.cxx_opts.append('-I"%s"' % util.get_workspace_dir(flux_dir))
            # add project cache dir in header-dirs
            self.cc_opts.append('-I"%s"' % self.cache_dir)
            self.cxx_opts.append('-I"%s"' % self.cache_dir)
            # add project apptype
            if self.target == 'windows':  #TODO: externalize this
                if self.toolchain == 'msvc':
                    if self.apptype == 'window':
                        self.ld_opts.append('-subsystem:windows')
                    else:
                        self.ld_opts.append('-subsystem:console')
                else:
                    if self.apptype in ['window', 'gui']:
                        self.ld_opts.append('-mwindows')
示例#4
0
def run(flux_dir, proj_dir, args):
    flux_dir = util.fix_path(flux_dir)
    proj_dir = util.fix_path(proj_dir)
    curr_dir = proj_dir

    # check space in project path
    if ' ' in proj_dir:
        log.warn(
            'whitespace in project path detected, `flux` will not work correctly'
        )

    # import verbs from flux dir
    verb.import_verbs(flux_dir)

    # parse args
    if len(args) <= 1:
        usage()
    else:
        name = args[1]
        args = args[2:]

        # run available verb
        if name in verb.verbs:
            verb.verbs[name].run(flux_dir, proj_dir, args)

        # for development stuff only
        elif name == 'dev':
            pass
        elif name == 'build':
            #log.text('===== test console output begin =====')
            #log.trace('trace message')
            #log.debug('debug message')
            #log.info('info message')
            #log.warn('warn message')
            #log.error('error message')
            #log.text('===== test console output end =====')

            # set build opts
            opts = BuildOpts()
            args = opts.parse_opts(proj_dir, args)

            # get target datas
            #TODO: add user custom targets

            # set target opts
            target = Target(flux_dir, opts)
            if target.toolchain == 'msvc':
                # check msvc install
                if target.find_msvc():
                    # update env vars
                    os.environ['PATH'] = os.environ[
                        'FLUX_MSVC_PATH'] + ';' + os.environ['PATH']
                    os.environ['INCLUDE'] = os.environ['FLUX_MSVC_INCLUDE']
                    os.environ['LIB'] = os.environ['FLUX_MSVC_LIB']
                else:
                    log.fatal('MSVC installation not found!')
            elif target.target == 'emscripten':
                from mods.sdks import emsdk
                if emsdk.sdk_dir_exists(flux_dir):
                    os.environ[
                        'PATH'] += os.pathsep + emsdk.get_emscripten_dir(
                            flux_dir)

            if opts.verbose >= 3:
                log.info('python %s.%s.%s' % (sys.version_info[:3]))
                log.info('os.environ:')
                for ev in sorted(
                        filter(lambda x: x.startswith('FLUX_'), os.environ)):
                    log.info('- %s: %s' %
                             (ev, log.YELLOW + os.environ[ev] + log.DEFAULT))

            # if not arg, set project in current dir
            if not len(args):
                args += [proj_dir]

            # for each projets
            for arg in args:
                print(log.YELLOW + ("===== `%s`" % arg) + log.DEFAULT)
                arg = util.fix_path(arg)
                path = os.path.join(proj_dir, arg)

                #print(path)

                proj = Project(flux_dir, path, opts)

                # change to project dir
                os.chdir(os.path.abspath(path))

                # clean output dir
                if opts.clean:
                    if opts.verbose >= 1:
                        log.info("cleaning `%s`" % proj.out_dir)
                    proj.clean()

                # create project intermediate dirs
                proj.make_dirs()

                # make Info.plist file
                if proj.target == 'macos' and proj.apptype == 'window':
                    proj.make_info_plist()

                # parse project file
                proj.parse_inputs()

                #print(proj)

                # depends
                if proj.build in ['app']:  # app only?
                    if len(proj.flux_libs):
                        sys_libs = []
                        for lib in proj.flux_libs:
                            # load dep project file
                            dep_dir = os.path.join(
                                util.get_workspace_dir(flux_dir), lib)
                            dep = Project(flux_dir, dep_dir, opts, True)

                            # module or library dep only
                            if dep.build in ['mod', 'lib']:
                                # change dir to dep dir
                                cd = os.getcwd()
                                os.chdir(os.path.abspath(dep_dir))

                                # parse dep project file
                                dep.parse_inputs()

                                # files stamp
                                out_file_stamp = None
                                gen_file_stamp = None

                                # check if module archive exists
                                if not os.path.exists(dep.out_file):
                                    if not os.path.exists(dep.gen_file):
                                        #create dep intermediate dirs
                                        dep.make_dirs()

                                        # generate ninja file for dep
                                        if opts.verbose >= 1:
                                            log.info('generate `%s`' %
                                                     dep.gen_file)
                                        with open(dep.gen_file, 'w') as out:
                                            dep.gen_ninja(out, opts, target)
                                else:
                                    #TODO: regenerate ninja if 'dep.flux_file' is most recent than 'dep.out_file'
                                    pass

                                # add 'dep.gen_file' to project
                                proj.ninja_files.append(dep.gen_file)

                                # build dep module
                                if os.path.exists(dep.gen_file):
                                    if opts.verbose >= 1:
                                        log.info(
                                            'building `%s` %s' %
                                            (dep.base_dir, log.BLUE +
                                             '(dependency)' + log.DEFAULT))
                                    dep.build_ninja(verbose=opts.verbose >= 3)

                                # add dep include dirs (abspath)
                                for inc in dep.include_dirs:
                                    proj.cc_opts.append('-I"%s"' % inc)
                                    proj.cxx_opts.append('-I"%s"' % inc)

                                # add dep module archive
                                proj.lib_files.append('"%s"' % dep.out_file)

                                # append system libs
                                for lib in dep.lib_files:
                                    if lib not in sys_libs:
                                        sys_libs.append(lib)

                                # return to main project
                                os.chdir(cd)

                        # add dep system libs at end
                        for lib in sys_libs:
                            if lib not in proj.lib_files:
                                proj.lib_files.append(lib)

                # set rules vars
                #rule_vars = target.get_rule_vars(proj)

                # generate ninja in proj.out_dir
                if opts.verbose >= 1:
                    log.info('generate `%s`' % proj.gen_file)
                with open(proj.gen_file, 'w') as out:  #StringIO()
                    proj.gen_ninja(out, opts, target)

                # build project from ninja
                if os.path.exists(proj.gen_file):
                    if opts.verbose >= 1:
                        log.info('building `%s`' % proj.base_dir)
                    proj.build_ninja(verbose=opts.verbose >= 3)
                    # copy assets files
                    proj.copy_assets(proj.asset_dir)
                    proj.copy_binaries(proj.out_dir)

            # return to start dir
            os.chdir(curr_dir)
        else:
            log.error('unknown verb `%s`' % name)
示例#5
0
    def gen_ninja(self, file, build_opts, target):
        '''generate ninja build file from project'''
        rules_remap = {
            'target.cmds.cc': '$cc',
            'target.cmds.cxx': '$cxx',
            'target.cmds.ar': '$ar',
            'target.cmds.as': '$as',
            'target.cmds.ld': '$ld',
            'target.opts.cc': '$cc_opts_target',
            'target.opts.cxx': '$cxx_opts_target',
            'target.opts.ar': '$ar_opts_target',
            'target.opts.as': '$as_opts_target',
            'target.opts.ld': '$ld_opts_target',
            'project.opts.cc': '$cc_opts_project',
            'project.opts.cxx': '$cxx_opts_project',
            'project.opts.ar': '$ar_opts_project',
            'project.opts.as': '$as_opts_project',
            'project.opts.ld': '$ld_opts_project',
            'project.out.file': '$out',
            'project.libs': '$libs',
            'project.objs': '$objs',
            'project.source': '$in',
            'project.source.dep': '$dep_file',
            'project.source.obj': '$out',  # $obj?
        }

        n = ninja.Writer(file, 150)

        n.comment('flux build system ' + VERSION)
        n.comment('repo: https://github.com/seyhajin/flux')
        n.comment('this file is generated automatically, do not edit!')
        n.newline()
        n.variable('ninja_required_version', ninja_required_version)
        n.newline()
        n.comment('project settings')
        n.variable('proj_name', self.name)
        n.variable('proj_dir', self.proj_dir)
        n.newline()
        n.comment('ninja settings')
        n.variable('builddir', self.cache_dir)
        if self.toolchain == 'msvc':
            if util.python_version[0] >= 3:
                n.variable('msvc_deps_prefix',
                           target.msvc_prefix.encode('utf-8').decode('utf-8'))
            else:
                n.variable('msvc_deps_prefix', unicode(target.msvc_prefix))
        n.newline()
        n.comment('target commands')
        n.variable('cc', target.cmds['cc'])
        n.variable('cxx', target.cmds['cxx'])
        n.variable('as', target.cmds['as'])
        n.variable('ar', target.cmds['ar'])
        n.variable('ld', target.cmds['ld'])
        n.newline()
        n.comment('target options')
        n.variable('cc_opts_target',
                   util.replace_env(target.opts['cc'], os.environ))
        n.variable('cxx_opts_target',
                   util.replace_env(target.opts['cxx'], os.environ))
        n.variable('as_opts_target',
                   util.replace_env(target.opts['as'], os.environ))
        n.variable('ar_opts_target',
                   util.replace_env(target.opts['ar'], os.environ))
        n.variable('ld_opts_target',
                   util.replace_env(target.opts['ld'], os.environ))
        n.newline()
        n.comment('project options')
        cc_opts = ' '.join(self.cc_opts)
        cxx_opts = ' '.join(self.cxx_opts)
        as_opts = ' '.join(self.as_opts)
        ar_opts = ' '.join(self.ar_opts)
        ld_opts = ' '.join(self.ld_opts)
        n.variable('cc_opts_project', util.replace_env(cc_opts, os.environ))
        n.variable('cxx_opts_project', util.replace_env(cxx_opts, os.environ))
        n.variable('as_opts_project', util.replace_env(as_opts, os.environ))
        n.variable('ar_opts_project', util.replace_env(ar_opts, os.environ))
        n.variable('ld_opts_project', util.replace_env(ld_opts, os.environ))

        #TODO: add `subninja` for depends modules : used to include another .ninja file, introduces a new scope
        # `include` used to include another .ninja file in the current scope (use this only for global configurations)
        #if len(self.ninja_files):
        #    n.newline()
        #    n.comment('project depends')
        #    for dep in self.ninja_files:
        #        n.subninja(dep)

        n.newline()
        n.comment('----------------------------')
        n.comment('RULES')
        n.comment('----------------------------')
        n.newline()

        #print(util.replace_env(target.rules['ar'], rules_remap))

        n.rule('cc_compile',
               util.replace_env(target.rules['cc'], rules_remap),
               deps=target.toolchain
               if target.toolchain in ['gcc', 'msvc'] else 'gcc',
               depfile=rules_remap['project.source.dep']
               if self.toolchain == 'gcc' else '',
               description='Compiling $in')
        n.newline()

        n.rule('cxx_compile',
               util.replace_env(target.rules['cxx'], rules_remap),
               deps=target.toolchain
               if target.toolchain in ['gcc', 'msvc'] else 'gcc',
               depfile=rules_remap['project.source.dep']
               if self.toolchain == 'gcc' else '',
               description='Compiling $in')
        n.newline()

        if 'rules' in self.data:
            if 'as' in self.date['rules']:
                n.rule(
                    'as_compile',
                    util.replace_env(target.rules['as'], rules_remap),
                    #deps=target.toolchain if target.toolchain in ['gcc', 'msvc'] else 'gcc',
                    description='Assembling $in')
                n.newline()

        if self.build in ['mod', 'module']:
            n.rule('archive',
                   util.replace_env(target.rules['ar'], rules_remap),
                   description='Archiving $out')
        elif self.build in ['app', 'application']:
            n.rule('link',
                   util.replace_env(target.rules['ld'], rules_remap),
                   description='Linking $out')
        else:
            log.fatal('ninja: unrecognized project build type: `%s`' %
                      self.build)

        n.newline()
        n.comment('----------------------------')
        n.comment('COMPILE')
        n.comment('----------------------------')
        n.newline()

        # for each project source file
        objs = []
        done = {}
        for src in self.src_files:
            ext = util.split_ext(src)
            obj = util.fix_path(
                os.path.join(self.cache_dir,
                             os.path.relpath(src, self.proj_dir)))
            dep = obj + '.d'
            obj += '.obj' if self.toolchain == 'msvc' else '.o'

            # relative paths
            src = util.fix_path(src.replace(self.proj_dir, '$proj_dir'))
            obj = util.fix_path(obj.replace(self.cache_dir, '$builddir'))
            dep = util.fix_path(dep.replace(self.cache_dir, '$builddir'))

            # check if already done
            if obj in done:
                log.fatal('OOPS! collision! contact me!')
            done[obj] = True

            # build statement
            if ext in ['.c', '.m']:
                n.build(obj,
                        'cc_compile',
                        src,
                        variables={'dep_file': dep}
                        if self.toolchain == 'gcc' else {})
            elif ext in ['.cc', '.cxx', '.cpp', '.c++', '.mm']:
                n.build(obj,
                        'cxx_compile',
                        src,
                        variables={'dep_file': dep}
                        if self.toolchain == 'gcc' else {})
            elif ext in ['.asm', '.s']:
                n.build(obj,
                        'as_compile',
                        src,
                        variables={'dep_file': dep}
                        if self.toolchain == 'gcc' else {})

            # add obj to objects list
            objs.append(obj)

        objs += self.obj_files

        # objects alias
        n.newline()
        n.comment('objects alias')
        n.build('objects', 'phony ' + ' '.join(objs))

        if self.build in ['mod', 'module']:
            n.newline()
            n.comment('----------------------------')
            n.comment('ARCHIVE')
            n.comment('----------------------------')
            n.newline()
            n.build(self.out_file,
                    'archive || objects',
                    '',
                    variables={
                        'libs': self.lib_files,
                        'objs': ' '.join(objs),
                    })
        elif self.build in ['app', 'application']:
            n.newline()
            n.comment('----------------------------')
            n.comment('LINK')
            n.comment('----------------------------')
            n.newline()
            n.build(self.out_file,
                    'link || objects',
                    '',
                    variables={
                        'libs': self.lib_files,
                        'objs': ' '.join(objs),
                    })
        else:
            log.fatal('ninja: unrecognized project build type: `%s`' %
                      self.build)

        # project alias
        n.newline()
        n.comment('project alias')
        n.build(self.name, 'phony ' + ninja.escape_path(self.out_file))

        n.newline()
        n.comment('----------------------------')
        n.comment('DEFAULT')
        n.comment('----------------------------')
        n.newline()
        n.build(
            'all',
            'phony %s %s' % (self.name, 'objects'),
        )
        n.newline()
        n.default('all')

        n.newline()
        n.close()