def copy_asset_files(self, files): for dst, src in files.items(): # create dest dir if not exist if not os.path.exists(util.split_dir(dst)): os.makedirs(util.split_dir(dst)) # copy file from src to dest copyfile(src, dst) # check if copy exist if os.path.exists(dst): continue log.fatal('error copying asset file `%s` to `%s`' % (src, dst))
def parse_system_input(self, path): '''parse system inputs''' name, ext = os.path.splitext(path) #if ext == '.flux': # print('....parsing module deps....') # # parsingModule.moduleDeps[name]=True # check if already imported if path in self.imported: return self.imported[path] = True # filter by extension if ext == '.a': if name.startswith('lib'): name = name[3:] if self.toolchain == 'msvc': self.lib_files.append(name + '.lib') else: self.lib_files.append('-l' + name) else: log.fatal( 'input error: `%s` - library file must be starts with `lib`and ends with `.a` extension.' ) elif ext == '.lib': if self.toolchain == 'msvc': self.lib_files.append(os.path.basename(path)) else: self.lib_files.append('-l' + name) elif ext == '.dylib': if self.toolchain == 'gcc': self.lib_files.append('-l' + name) elif ext == '.framework': if self.toolchain == 'gcc': self.lib_files.append('-framework ' + name) elif ext == '.weak_framework': if self.toolchain == 'gcc': self.lib_files.append('-weak_framework ' + name) elif ext in ['.h', '.hh', '.hxx', '.hpp', '.h++']: pass # ignore elif ext == '.flux': self.flux_libs.append(name) else: log.fatal('unrecognized input file type: `%s`' % path)
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
def find_msvc(self): def find_max_ver_dir(ver_dir): max_ver = 0 max_ver_dir = '' for f in os.listdir(ver_dir): ver_dir = os.path.join(ver_dir, f) if not os.path.isdir(ver_dir): continue ver = int(f.replace('.', '')) if ver > max_ver: max_ver = ver max_ver_dir = ver_dir return max_ver_dir if self.toolchain == 'msvc': # MSVC msvcs = '' for ver in self.msvc_version: msvcs = os.path.join(os.environ['ProgramFiles(x86)'], 'Microsoft Visual Studio', str(ver).strip()) if os.path.isdir(msvcs): break if not os.path.isdir(msvcs): return False # Windows Kits wkits = os.path.join(os.environ['ProgramFiles(x86)'], 'Windows Kits\\10') if not os.path.isdir(wkits): return False # VC Tools tools_dir = '' max_ver = 0 for f in os.listdir(msvcs): d = os.path.join(msvcs, f, 'VC\\Tools\\MSVC') if not os.path.isdir(d): continue for ff in os.listdir(d): ver_dir = os.path.join(d, ff) if not os.path.isdir(ver_dir): continue ver = int(ff.replace('.', '')) if ver > max_ver: tools_dir = ver_dir max_ver = ver # VC Includes incs_dir = find_max_ver_dir(os.path.join(wkits, 'Include')) if not incs_dir: return False # VC Libs libs_dir = find_max_ver_dir(os.path.join(wkits, 'Lib')) if not libs_dir: return False # show msvc dirs if self.buildopts.verbose >= 3: log.info('MSVC installation auto-detected: %s"%s"%s' % (log.YELLOW, msvcs, log.DEFAULT)) log.info('- VC Tools = %s"%s"%s' % (log.YELLOW, tools_dir, log.DEFAULT)) log.info('- VC Include = %s"%s"%s' % (log.YELLOW, incs_dir, log.DEFAULT)) log.info('- VC Lib = %s"%s"%s' % (log.YELLOW, libs_dir, log.DEFAULT)) # set msvc env vars if self.buildopts.arch == 'x86': os.environ['FLUX_MSVC_PATH'] = tools_dir+'\\bin\\Hostx86\\x86' os.environ['FLUX_MSVC_INCLUDE'] = tools_dir+'\\include;'+incs_dir+'\\ucrt;'+incs_dir+'\\shared;'+incs_dir+'\\um;'+incs_dir+'\\winrt' os.environ['FLUX_MSVC_LIB'] = tools_dir+'\\lib\\x86;'+libs_dir+'\\ucrt\\x86;'+libs_dir+'\\um\\x86' elif self.buildopts.arch == 'x64': os.environ['FLUX_MSVC_PATH'] = tools_dir+'\\bin\\Hostx64\\x64' os.environ['FLUX_MSVC_INCLUDE'] = tools_dir+'\\include;'+incs_dir+'\\ucrt;'+incs_dir+'\\shared;'+incs_dir+'\\um;'+incs_dir+'\\winrt' os.environ['FLUX_MSVC_LIB'] = tools_dir+'\\lib\\x64;'+libs_dir+'\\ucrt\\x64;'+libs_dir+'\\um\\x64' else: log.fatal('unrecognized architecture build option `%s`' % self.opts.arch) return True else: return False
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)
def parse_local_input(self, path): '''parse local inputs''' # check if already imported if path in self.imported: return self.imported[path] = True # assets i = path.find('@/') if i != -1: src = path[:i] if not os.path.exists(src): log.error('asset `%s` not found' % src) return self.asset_files.append(path) return # get name and extension name, ext = os.path.splitext(path) name = util.split_name(path) # filter by name if name == '*': path_dir = os.path.dirname(os.path.abspath(path)) # check dir if not os.path.isdir(path_dir) and not path_dir.endswith('**'): log.fatal('input directory `%s` not found' % path_dir) return # filter by extension if ext == '.h': self.include_dirs.append(path_dir) self.cc_opts.append('-I"%s"' % path_dir) self.cxx_opts.append('-I"%s"' % path_dir) elif ext in ['.hh', '.hxx', '.hpp', '.h++']: self.include_dirs.append(path_dir) self.cxx_opts.append('-I"%s"' % path_dir) elif ext in ['.a', '.lib']: if self.toolchain == 'msvc': self.library_dirs.append(path_dir) self.ld_opts.append('-LIBPATH:"%s"' % path_dir) else: self.library_dirs.append(path_dir) self.ld_opts.append('-L"%s"' % path_dir) elif ext == '.dylib': if self.toolchain == 'gcc': self.library_dirs.append(path_dir) self.ld_opts.append('-L"%s"' % path_dir) elif ext == '.framework': if self.toolchain == 'gcc': self.library_dirs.append(path_dir) self.ld_opts.append('-F"%s"' % path_dir) elif ext in [ '.c', '.cc', '.cxx', '.cpp', '.c++', '.m', '.mm', '.asm', '.s' ]: srcs = [] if sys.version_info[:2] >= (3, 5): srcs = glob.glob(os.path.join(path_dir, name + ext), recursive=True) else: # recursive if path_dir.endswith('**'): path_dir = path_dir[:-3] for dirpaths, _, files in sorted(os.walk(path_dir)): for fn in fnmatch.filter(files, name + ext): srcs.append(os.path.join(dirpaths, fn)) else: srcs = glob.glob(os.path.join(path_dir, name + ext)) if srcs: for src in sorted(srcs): if src in self.imported: continue self.imported[src] = True self.src_files.append(os.path.abspath(src)) else: log.fatal('unrecognized input file filter `%s%s`' % (name, ext)) return # filter by extension if ext == '.framework': if self.toolchain == 'gcc': if not os.path.isdir(path): log.fatal('input framework not found "%s"' % path) return elif ext == '.flux': log.fatal( 'input: flux module must be defined as a "system" input: `<%s>`' % path) return elif '$(TARGET_ARCH' not in path: if os.path.isdir(path): self.asset_files.append(path) return elif not os.path.isfile(path): log.fatal('input file not found "%s"' % path) if ext in ['.h', '.hh', '.hxx', '.hpp', '.h++']: pass # ignore elif ext in [ '.c', '.cc', '.cxx', '.cpp', '.c++', '.m', '.mm', '.asm', '.s' ]: self.src_files.append(os.path.abspath(path)) elif ext == '.java': if self.target == 'android': self.java_files.append(path) elif ext == '.o': self.obj_files.append(path) elif ext == '.lib': self.lib_files.append(path) elif ext == '.a': if self.toolchain == 'gcc': self.lib_files.append('"%s"' % path) elif ext in ['.so', '.dylib']: if self.toolchain == 'gcc': self.lib_files.append('"%s"' % path) self.bin_files.append(path) elif ext in ['.exe', '.dll']: if self.target == 'windows': self.bin_files.append(path) elif ext == '.framework': if self.toolchain == 'gcc': #TODO: Ugly! #parse_local_input(extract_dir(path)+'/*.framework') #parse_system_input(strip_dir(path)) #self.bin_files.append(path) pass else: self.asset_files.append(path)
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')
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()