# # Collective Knowledge (checking and installing software) # # See CK LICENSE.txt for licensing details # See CK COPYRIGHT.txt for copyright details # # Developer: Grigori Fursin, Grigori.Fursin@cTuning.org, http://fursin.net # cfg={} # Will be updated by CK (meta description of this module) work={} # Will be updated by CK (temporal data) ck=None # Will be updated by CK (initialized CK kernel) # Local settings env_install_path='CK_TOOLS' env_search='CK_DIRS' ############################################################################## # Initialize module def init(i): """ Input: {} Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ return {'return':0} ############################################################################## # detect is given software is already installed and register it in the CK or install it if package exists def detect(i): """ See "check" API """ return check(i) ############################################################################## # detect soft (internal function - gradually outdated) def internal_detect(i): """ Input: { (host_os) - host OS (detect, if omitted) (target_os) - target OS (detect, if omitted) (target_device_id) - target device ID (detect, if omitted) (data_uoa) or (uoa) - software UOA entry or (tags) - search UOA by tags (separated by comma) (tool) - force this tool name (env) - if !='', use this env string before calling compiler (to set up env) (show) - if 'yes', show output } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 version_str - version as string version_lst - version as list of strings version_raw - raw list of strings (output of --version) } """ import os o=i.get('out','') # Check host/target OS/CPU hos=i.get('host_os','') tos=i.get('target_os','') tdid=i.get('target_device_id','') r=ck.access({'action':'detect', 'module_uoa':cfg['module_deps']['platform.os'], 'host_os':hos, 'target_os':tos, 'target_device_id':tdid, 'skip_info_collection':'yes'}) if r['return']>0: return r hos=r['host_os_uid'] hosx=r['host_os_uoa'] hosd=r['host_os_dict'] tos=r['os_uid'] tosx=r['os_uoa'] tosd=r['os_dict'] hplat=hosd['ck_name'] tplat=tosd['ck_name'] env=i.get('env','') ubtr=hosd.get('use_bash_to_run','') svarb=hosd.get('env_var_start','') svarb1=hosd.get('env_var_extra1','') svare=hosd.get('env_var_stop','') svare1=hosd.get('env_var_extra2','') sexe=hosd.get('set_executable','') sbp=hosd.get('bin_prefix','') envsep=hosd.get('env_separator','') scall=hosd.get('env_call','') sext=hosd.get('script_ext','') # Check soft UOA duoa=i.get('uoa','') if duoa=='': duoa=i.get('data_uoa','') if duoa=='': # Search tags=i.get('tags','') if tags!='': r=ck.access({'action':'search', 'module_uoa':work['self_module_uid'], 'tags':tags}) if r['return']>0: return r l=r['lst'] if len(l)>0: duid=l[0].get('data_uid') duoa=duid if duoa=='': return {'return':1, 'error':'software entry was not found'} # Load r=ck.access({'action':'load', 'module_uoa':work['self_module_uid'], 'data_uoa':duoa}) if r['return']>0: return r d=r['dict'] p=r['path'] duoa=r['data_uoa'] duid=r['data_uid'] if o=='con': x=duoa if duid!=duoa: x+=' ('+duid+')' ck.out('Software description entry found: '+x) # Check if customize script is redirected into another entry: # another_entry_with_customize_script=d.get('use_customize_script_from_another_entry', None) if another_entry_with_customize_script: r=ck.access({'action':'find', 'module_uoa': another_entry_with_customize_script.get('module_uoa', work['self_module_uid']), 'data_uoa': another_entry_with_customize_script.get('data_uoa','') }) if r['return']>0: return r customization_script_path = r['path'] else: customization_script_path = p cs=None rx=ck.load_module_from_path({'path':customization_script_path, 'module_code_name':cfg['custom_script_name'], 'skip_init':'yes'}) if rx['return']==0: cs=rx['code'] elif another_entry_with_customize_script or not rx['error'].startswith("can't find module code"): return rx # Checking name cus=d.get('customize',{}) tool=i.get('tool','') if tool=='': if cus.get('soft_file_as_env','')!='': tool=svarb+cus['soft_file_as_env']+svare if cus.get('soft_file_not_tool','')!='yes': ry=prepare_target_name({'host_os_dict':hosd, 'target_os_dict':tosd, 'cus':cus}) if ry['return']>0: return ry tool=ry['tool'] # Preparing CMD soft_version_cmd=cus.get('soft_version_cmd',{}).get(hplat,'') if o=='con': ck.out('') ck.out('Prepared cmd: '+soft_version_cmd+' ...') # Check version (via customized script) ... ver='' lst=[] ii={'full_path':tool, 'bat':env, 'host_os_dict':hosd, 'target_os_dict':tosd, 'cmd':soft_version_cmd, 'use_locale':cus.get('use_locale_for_version',''), 'customize':cus, 'custom_script_obj':cs, 'data_uid': duid } if ck.cfg.get('minimize_soft_detect_output','')!='yes': ii['out']=o rx=get_version(ii) if rx['return']==0: ver=rx['version'] lst=rx['version_lst'] if ver=='': return {'return':16, 'error':'version was not detected'} # Split version rx=split_version({'version':ver}) if rx['return']>0: return rx sver=rx['version_split'] if i.get('show','')=='yes': ck.out('Output:') ck.out('') for q in lst: ck.out(' '+q) if o=='con': ck.out('') ck.out('Version detected: '+ver) return {'return':0, 'version_str':ver, 'version_lst':sver, 'version_raw':lst} ############################################################################## # setup environment for a given software - # it is a low level routine which ask you the exact path to the tool and its version def setup(i): """ Input: { (host_os) - host OS (detect, if omitted) (target_os) - target OS (detect, if omitted) (target_device_id) - target device ID (detect, if omitted) (data_uoa) or (uoa) - soft configuration UOA or (tags) - search UOA by tags (separated by comma) (soft_name) - use this user friendly name for environment entry (soft_add_name) - add extra name to above name (such as anaconda) (customize) - dict with custom parameters (usually passed to customize script) skip_add_dirs skip_add_to_path skip_add_to_bin skip_add_to_ld_path add_include_path skip_path - skiping installation path (for local versions) version - add this version skip_version - if 'yes', do not add version (skip_path) - skiping installation path (for local versions) (env) - update default env with this dict (ienv) - supply extra install_env overrides via this mechanism (deps) - list with dependencies (in special format, possibly resolved (from package)) (install_path) - path with soft is installed (full_path) - full path to a tool or library (install_path will be calculated automatically) (bat_file) - if !='', record environment to this bat file, instead of creating env entry (quiet) - if 'yes', minimize questions (env_data_uoa) - use this data UOA to record (new) env (env_repo_uoa) - use this repo to record new env (env_new) - if 'yes', do not search for environment (was already done in package, for example) (package_uoa) - if called from package, record package_uoa just in case (reset_env) - if 'yes', do not use environment from existing entry, but use original one (extra_version) - add extra version, when registering software (for example, -trunk-20160421) (skip_device_info_collection) - if 'yes', do not collect device info } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 env_data_uoa - environment entry UOA env_data_uid - environment entry UID deps - resolved dependencies (if any) } """ import os import json o=i.get('out','') env_new=i.get('env_new','') ######################################################################## # Check host/target OS/CPU hos=i.get('host_os','') tos=i.get('target_os','') tdid=i.get('target_device_id','') if tdid=='': tdid=i.get('device_id','') ii={'action':'detect', 'module_uoa':cfg['module_deps']['platform.os'], 'host_os':hos, 'target_os':tos, 'target_device_id':tdid, 'skip_info_collection':'no'} if i.get('skip_device_info_collection','')=='yes': ii['skip_info_collection']='yes' r=ck.access(ii) if r['return']>0: return r features=r.get('features',{}) hos=r['host_os_uid'] hosx=r['host_os_uoa'] hosd=r['host_os_dict'] tos=r['os_uid'] tosx=r['os_uoa'] tosd=r['os_dict'] tbits=tosd.get('bits','') hplat=hosd['ck_name'] tplat=tosd['ck_name'] # Check if base is different x1=hosd.get('base_uid','') x2=hosd.get('base_uoa','') if x1!='' and x2!='': hos=x1 hosx=x2 x1=tosd.get('base_uid','') x2=tosd.get('base_uoa','') if x1!='' and x2!='': tos=x1 tosx=x2 # Check soft UOA duoa=i.get('uoa','') if duoa=='': duoa=i.get('data_uoa','') tags=i.get('tags','') if duoa=='': xcids=i.get('xcids',[]) if len(xcids)>0: duoa=xcids[0].get('data_uoa','') duid=duoa if duoa=='' and tags!='': r=ck.access({'action':'search', 'module_uoa':work['self_module_uid'], 'tags':tags}) if r['return']>0: return r l=r['lst'] if len(l)>0: duid=l[0].get('data_uid') duoa=duid d={} p='' ######################################################################## if duoa=='': # Try to detect CID in current path rx=ck.detect_cid_in_current_path({}) if rx['return']==0: duoa=rx.get('data_uoa','') if duoa!='': # Load defined or found soft entry r=ck.access({'action':'load', 'module_uoa':work['self_module_uid'], 'data_uoa':duoa}) if r['return']>0: return r d=r['dict'] p=r['path'] duoa=r['data_uoa'] duid=r['data_uid'] if duoa=='': try: p=os.getcwd() except OSError: os.chdir('..') p=os.getcwd() pc=os.path.join(p, ck.cfg['subdir_ck_ext'], ck.cfg['file_meta']) found=False if os.path.isfile(pc): r=ck.load_json_file({'json_file':pc}) if r['return']==0: d=r['dict'] found=True if not found: return {'return':1, 'error':'software UOA (data_uoa) is not defined'} if o=='con': if duoa!='' and duid!='': x=': '+duoa if duid!=duoa: x+=' ('+duid+')' else: x=' in local directory' ck.out(' Software entry found'+x) # Check deps, customize, install path ltags=d.get('tags',[]) deps=d.get('deps',{}) env=d.get('env',{}) cus=d.get('customize',{}) pi='' csp=d.get('can_skip_path','') extra_version=i.get('extra_version', cus.get('extra_version','')) # Add tags from the search! for q in tags.split(','): q1=q.strip() if q1!='' and q1 not in ltags: ltags.append(q1) # Finish tags tg='host-os-'+hosx if tg not in ltags: ltags.append(tg) tg='target-os-'+tosx if tg not in ltags: ltags.append(tg) tg=tbits+'bits' if tg not in ltags: ltags.append(tg) ######################################################################## # Check if environment already set (preload to update) enduoa=i.get('env_data_uoa','') enruoa=i.get('env_repo_uoa','') update=False if enduoa!='': rx=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['env'], 'data_uoa':enduoa, 'repo_uoa':enruoa}) if rx['return']==0: update=True edx=rx['dict'] cus.update(edx.get('customize',{})) deps=edx.get('deps',{}) if i.get('reset_env','')!='yes' and 'tmp' not in edx.get('tags',[]): env=edx.get('env',{}) pi=cus.get('path_install','') # Update from input udeps=i.get('deps',{}) deps.update(udeps) uenv=i.get('env',{}) env.update(uenv) ucus=i.get('customize',{}) cus.update(ucus) envp=cus.get('env_prefix','') envps=envp+'_SET' if i.get('soft_name','')!='': # (direct input overrides meta-data) dname = i['soft_name'] else: dname = d.get('soft_name','') + cus.get('package_extra_name', '') dname += i.get('soft_add_name','') ienv=i.get('ienv',{}) # override install_env using command-line options for ienv_key in ienv: if 'install_env' not in cus: # manual vivification cus['install_env'] = {} cus['install_env'][ienv_key] = ienv[ienv_key] pi1=i.get('install_path','') if pi1!='': pi=pi1 fp=i.get('full_path','') ######################################################################## # Check meta setup={'host_os_uoa':hos, 'target_os_uoa':tos, 'target_os_bits':tbits} # Resolve deps (if not ignored, such as when installing local version with all dependencies set) if cus.get('ignore_deps','')=='yes': deps={} sdeps='' sdeps1='' if len(deps)>0: ii={'action':'resolve', 'module_uoa':cfg['module_deps']['env'], 'host_os':hos, 'target_os':tos, 'target_device_id':tdid, 'repo_uoa':enruoa, 'deps':deps} if o=='con': ii['out']='con' rx=ck.access(ii) if rx['return']>0: return rx sdeps=rx['bat'] sdeps1=rx['cut_bat'] deps=rx['deps'] # Update deps (add UOA) for q in deps: v=deps[q] vuoa=v.get('uoa','') # can be undefined if OS specific if vuoa!='': setup['deps_'+q]=vuoa # Check if customize script is redirected into another entry: # another_entry_with_customize_script=d.get('use_customize_script_from_another_entry', None) if another_entry_with_customize_script: r=ck.access({'action':'find', 'module_uoa': another_entry_with_customize_script.get('module_uoa', work['self_module_uid']), 'data_uoa': another_entry_with_customize_script.get('data_uoa','') }) if r['return']>0: return r customization_script_path = r['path'] else: customization_script_path = p cs=None rx=ck.load_module_from_path({'path':customization_script_path, 'module_code_name':cfg['custom_script_name'], 'data_uoa':duoa ,'cfg':d, 'skip_init':'yes'}) if rx['return']==0: cs=rx['code'] elif another_entry_with_customize_script or not rx['error'].startswith("can't find module code"): return rx ######################################################################## ######################################################################## ######################################################################## ######################################################################## # Starting processing soft # Check via full path first if pi=='' and fp=='' and o=='con' and cus.get('skip_path','')!='yes' and i.get('skip_path','')!='yes' and not update: ck.out('') ry=prepare_target_name({'host_os_dict':hosd, 'target_os_dict':tosd, 'cus':cus}) if ry['return']>0: return ry sname=ry['tool'] y0='installed library, tool or script' if sname!='': suname=d.get('soft_name','') if cus.get('skip_soft_file_is_asked','')=='yes': if suname!='': y0=suname else: y0=sname if suname!='': y0=suname+' ('+sname+')' y1='full path to '+y0 y2='' y3=cus.get('soft_path_example',{}).get(hplat,'') if y3!='': y2=' (example: '+y3+')' r=ck.inp({'text':'Enter '+y1+y2+': '}) fp=r['string'].strip() # Check if file really exists and check version if a tool ver=cus.get('version','') vercus=ver if fp!='': if cus.get('skip_file_check','')!='yes' and not os.path.exists(fp): return {'return':1, 'error':'software not found in a specified path ('+fp+')'} skip_existing='no' if cus.get('force_cmd_version_detection','')=='yes': skip_existing='yes' ver='' if ver=='': soft_version_cmd=cus.get('soft_version_cmd',{}).get(hplat,'') if o=='con': ck.out('') ck.out(' Attempting to detect version automatically (if supported) ...') # Check version (via customized script) ... ii={'full_path':fp, 'bat':sdeps, 'host_os_dict':hosd, 'target_os_dict':tosd, 'cmd':soft_version_cmd, 'customize':cus, 'custom_script_obj':cs, 'skip_existing':skip_existing, 'skip_add_target_file':cus.get('soft_version_skip_add_target_file',''), 'use_locale':cus.get('use_locale_for_version',''), 'data_uid': duid, 'deps': deps, } if ck.cfg.get('minimize_soft_detect_output','')!='yes': ii['out']=o rx=get_version(ii) if rx['return']==0: ver=rx['version'] if o=='con': ck.out('') ck.out(' Detected version: '+ver) elif rx['return']!=16 and rx['return']!=22: return rx else: if o=='con': ck.out('') ck.out(' WARNING: didn\'t manage to automatically detect software version!') ######################################################################## # Get various git info ... ss1='' ss2='' ss3='' ss4='' ss5='' ver_to_search=ver if cus.get('use_git_revision','')=='yes': import datetime psrc=cus.get('git_src_dir','') dfp=i.get('full_path_install','') if dfp!='': if psrc!='': dfp=os.path.join(dfp, psrc) try: pwd1=os.getcwd() except OSError: os.chdir('..') pwd1=os.getcwd() if os.path.isdir(dfp): os.chdir(dfp) r=ck.access({'action':'run_and_get_stdout', 'module_uoa':cfg['module_deps']['os'], 'cmd':['git','rev-parse','--short','HEAD']}) if r['return']==0 and r['return_code']==0: ss1=r['stdout'].strip() r=ck.access({'action':'run_and_get_stdout', 'module_uoa':cfg['module_deps']['os'], 'cmd':['git','log','-1','--format=%cd']}) if r['return']==0 and r['return_code']==0: ss2=r['stdout'].strip() if ss2!='': ss2x=ss2 j=ss2x.find(' +') if j<0: j=ss2x.find(' -') if j>0: ss2x=ss2[:j] x=datetime.datetime.strptime(ss2x, '%a %b %d %H:%M:%S %Y') ss3=x.isoformat() ss4=ss3[:10].replace('-','') if ss1!='': ss5=ss4+'-'+ss1 if 'git_info' not in cus: cus['git_info']={} cus['git_info']['revision']=ss1 cus['git_info']['datetime']=ss2 cus['git_info']['iso_datetime']=ss3 cus['git_info']['iso_datetime_cut']=ss4 cus['git_info']['iso_datetime_cut_revision']=ss5 if o=='con': ck.out('') if ss1!='': ck.out('Detected GIT revision: '+ss1) if ss2!='': ck.out('Detected GIT date time of last commit: '+ss2) os.chdir(pwd1) ver+='-'+ss1 ######################################################################## # Ask for version if was not detected or is not explicitly specified (for example, from a package) if ver=='' and cus.get('skip_version','')!='yes' and o=='con': ck.out('') r=ck.inp({'text':'Enter version of this software (for example, 3.21.6-2 or press Enter if default/unknown): '}) ver=r['string'].strip().lower() ver_to_search=ver # Add extra, if needed (useful for changing trunks) if extra_version!='': ver+=extra_version ver_to_search+=extra_version # If customized version has changed, try to check env again ... if vercus!=ver: env_new='no' # Split version rx=split_version({'version':ver}) if rx['return']>0: return rx sver=rx['version_split'] # Add version to setup and separate into tags setup['version']=ver_to_search setup['version_split']=sver # Prepare tags from version if ver!='': x='' for q in sver: if x!='':x+='.' x+=str(q) tg='v'+x if tg not in ltags: ltags.append(tg) unsplit_version_tag_prefix = cus.get('unsplit_version_to_tags_prefixed_with') if unsplit_version_tag_prefix != None: # NB: empty string is treated differently from absence! ltags.append( unsplit_version_tag_prefix + ver_to_search ) tags_csv = ','.join( [t.strip() for t in ltags if t] ) ######################################################################## # Search if environment is already registered for this version # (to delete or reuse it) finish=False if enduoa=='' and env_new!='yes': if o=='con': ck.out('') ck.out('Searching if environment already exists using:') ck.out(' * Tags: '+tags_csv) if len(deps)>0: for q in deps: v=deps[q] vuoa=v.get('uoa','') if vuoa!='': ck.out(' * Dependency: '+q+'='+v.get('uoa','')) r=ck.access({'action':'search', 'module_uoa':cfg['module_deps']['env'], 'repo_uoa':enruoa, 'tags':tags_csv, 'search_dict':{'setup':setup}}) if r['return']>0: return r lst=r['lst'] if len(lst)>0: fe=lst[0] enduoa=fe['data_uoa'] enduid=fe['data_uid'] if o=='con': x=enduoa if enduid!=enduoa: x+=' ('+enduid+')' ck.out('') ck.out('Environment already registered for this version: '+x) update=False if i.get('update','')=='yes': update=True if not update: if o=='con': ck.out('') if i.get('quiet','')=='yes': dl='y' else: r=ck.inp({'text':'Would you like to delete this entry and re-register environment (Y/n): '}) dl=r['string'].strip().lower() if dl=='' or dl=='y' or dl=='yes': update=False rx=ck.access({'action':'delete', 'module_uoa':cfg['module_deps']['env'], 'data_uoa':enduoa, 'repo_uoa':enruoa}) if rx['return']>0: return rx else: ck.out('') r=ck.inp({'text':'Would you like to update this entry (Y/n): '}) upd=r['string'].strip().lower() if upd=='' or upd=='y' or upd=='yes': update=True else: finish=True if update: rx=ck.access({'action':'load', 'module_uoa':cfg['module_deps']['env'], 'data_uoa':enduoa, 'repo_uoa':enruoa}) if rx['return']>0: return rx edx=rx['dict'] cus1=edx.get('customize',{}) deps1=edx.get('deps',{}) env1=edx.get('env',{}) cus.update(cus1) deps.update(deps1) env.update(env1) pi=cus.get('path_install','') else: if o=='con': ck.out('') ck.out(' Environment with above tags is not yet registered in CK ...') ############################################################ if not finish: # Prepare environment and batch sb='' if o=='out': ck.out('') ck.out('Preparing environment and batch file ...') sdirs=hosd.get('dir_sep','') wb=tosd.get('windows_base','') rem=hosd.get('rem','') eset=hosd.get('env_set','') svarb=hosd.get('env_var_start','') svare=hosd.get('env_var_stop','') evs=hosd.get('env_var_separator','') eifs=hosd.get('env_quotes_if_space','') ellp=hosd.get('env_ld_library_path','') if ellp=='': ellp='LD_LIBRARY_PATH' elp=hosd.get('env_library_path','') if elp=='': elp='LIBRARY_PATH' # Check installation path if fp=='' and cus.get('skip_path','')!='yes' and i.get('skip_path','')!='yes' and not update: if o=='con': # if update: # ck.out('') # ck.out('Current path to installed tool: '+pi) # r=ck.inp({'text':'Input new path to installed tool or press Enter to keep old: '}) # pix=r['string'].strip() # if pix!='': pi=pix if pi=='': ck.out('') ye=cus.get('input_path_example','') if ye!='': y=' (example: '+ye+')' else: y='' y1=cus.get('input_path_text','') if y1=='': y1='path to installed software (root directory possibly pointing to bin, lib, include, etc)' r=ck.inp({'text':'Enter '+y1+y+': '}) pi=r['string'].strip().strip('"') ipr=cus.get('input_path_remove','') if ipr!='' and ipr>0: for q in range(0,ipr): try: pi=os.path.split(pi)[0] except: pass if pi=='' and csp!='yes': return {'return':1, 'error':'installation path is not specified'} if fp!='': cus['full_path']=fp if pi!='': cus['path_install']=pi ### OLD start if cus.get('skip_add_dirs','')!='yes' and pi!='': if cus.get('add_include_path','')=='yes' and cus.get('path_include','')=='': pii=pi+sdirs+'include' cus['path_include']=pii if cus.get('skip_add_to_bin','')!='yes': pib=pi if cus.get('skip_add_bin_ext','')!='yes': pib+=sdirs+'bin' cus['path_bin']=pib if cus.get('skip_add_to_ld_path','')!='yes' and cus.get('path_lib','')=='': plib=pi+sdirs+'lib64' if not os.path.isdir(plib): plib=pi+sdirs+'lib32' if not os.path.isdir(plib): plib=pi+sdirs+'lib' if not os.path.isdir(plib): return {'return':1, 'error':'can\'t find lib path'} cus['path_lib']=plib else: cus['skip_path']='yes' ### OLD stop # If install path has space, add quotes for some OS ... xs='' if pi.find(' ')>=0 and eifs!='': xs=eifs # Check if customize script is redirected into another entry: # another_entry_with_customize_script=d.get('use_customize_script_from_another_entry', None) if another_entry_with_customize_script: r=ck.access({'action':'find', 'module_uoa': another_entry_with_customize_script.get('module_uoa', work['self_module_uid']), 'data_uoa': another_entry_with_customize_script.get('data_uoa','') }) if r['return']>0: return r customization_script_path = r['path'] else: customization_script_path = p cs=None rx=ck.load_module_from_path({'path':customization_script_path, 'module_code_name':cfg['custom_script_name'], 'data_uoa':duoa ,'cfg':d, 'skip_init':'yes'}) if rx['return']==0: cs=rx['code'] elif another_entry_with_customize_script or not rx['error'].startswith("can't find module code"): return rx sadd='' if cs!=None and 'setup' in dir(cs): # Prepare info rx=ck.gen_tmp_file({}) if rx['return']>0: return rx fn=rx['file_name'] # Call setup script ii={"host_os_uoa":hosx, "host_os_uid":hos, "host_os_dict":hosd, "target_os_uoa":tosx, "target_os_uid":tos, "target_os_dict":tosd, "target_device_id":tdid, "soft_uoa":duoa, "soft_name":dname, "tags":ltags, "cfg":d, "env":env, "deps":deps, "deps_copy":i.get('deps_copy',{}), "customize":cus, "self_cfg":cfg, "version":ver, "version_split":sver, "features":features, "ck_kernel":ck } if o=='con': ii['interactive']='yes' if i.get('quiet','')=='yes': ii['interactive']='' rx=cs.setup(ii) if rx['return']>0: return rx sadd=rx['bat'] pi=cus.get('path_install','') if cus.get('soft_name','')!='': dname=cus['soft_name'] if cus.get('soft_extra_name','')!='': dname+=cus['soft_extra_name'] ######################################################### # Finish batch sb+=hosd.get('batch_prefix','')+'\n' check_if_set=hosd.get('batch_check_if_set','') if check_if_set!='': sb+=check_if_set.replace('$#ck_var#$',envps)+'\n' x=duoa if duid!=duoa: x+=' ('+duid+') ' if len(tags)>0: y='' for q in ltags: if y!='': y+=',' y+=q x+=' ('+y+')' sb+=rem+' '+'Soft UOA = '+x+'\n' sb+=rem+' '+'Host OS UOA = '+hosx+' ('+hos+')\n' sb+=rem+' '+'Target OS UOA = '+tosx+' ('+tos+')\n' sb+=rem+' '+'Target OS bits = '+tbits+'\n' if ver!='': sb+=rem+' '+'Tool version = '+ver+'\n' cus['version']=ver if len(sver)>0: sb+=rem+' '+'Tool split version = '+json.dumps(sver)+'\n' cus['version_split']=sver sb+='\n' if sdeps!='': sb+=rem+' Dependencies:\n' sb+=sdeps1+'\n' if cus.get('skip_path','')!='yes' and i.get('skip_path','')!='yes' and pi!='': sb+=eset+' '+envp+'='+xs+pi+xs+'\n' cus['path_install']=pi envp_b=envp+'_BIN' pib=cus.get('path_bin','') envp_l=envp+'_LIB' plib=cus.get('path_lib','') envp_i=envp+'_INCLUDE' piib=cus.get('path_include','') if cus.get('skip_add_dirs','')!='yes': # and pi!='': if pib!='' and cus.get('skip_add_to_bin','')!='yes': sb+=eset+' '+envp_b+'='+xs+pib+xs+'\n' if hplat=='win': rcc=ck.access({'action':'convert_to_cygwin_path', 'module_uoa':cfg['module_deps']['os'], 'path':pib}) if rcc['return']==0: sb+=eset+' '+envp_b+'_CYGWIN='+xs+rcc['path']+xs+'\n' if plib!='': sb+=eset+' '+envp_l+'='+xs+plib+xs+'\n' if hplat=='win': rcc=ck.access({'action':'convert_to_cygwin_path', 'module_uoa':cfg['module_deps']['os'], 'path':plib}) if rcc['return']==0: sb+=eset+' '+envp_l+'_CYGWIN='+xs+rcc['path']+xs+'\n' if piib!='': sb+=eset+' '+envp_i+'='+xs+piib+xs+'\n' if hplat=='win': rcc=ck.access({'action':'convert_to_cygwin_path', 'module_uoa':cfg['module_deps']['os'], 'path':piib}) if rcc['return']==0: sb+=eset+' '+envp_i+'_CYGWIN='+xs+rcc['path']+xs+'\n' if sadd!='': sb+='\n'+sadd # Add all env for k in sorted(env): v=str(env[k]) if eifs!='' and wb!='yes': if v.find(' ')>=0 and not v.startswith(eifs): v=eifs+v+eifs sb+=eset+' '+k+'='+v+'\n' sb+='\n' # Add to existing vars if cus.get('add_to_path','')=='yes' or (cus.get('skip_add_to_path','')!='yes' and cus.get('skip_add_to_bin','')!='yes' and cus.get('skip_dirs','')!='yes' and pi!=''): sb+=eset+' PATH='+svarb+envp_b+svare+evs+svarb+'PATH'+svare+'\n' if pi!='' and cus.get('skip_add_to_ld_path','')!='yes' and cus.get('skip_dirs','')!='yes': sb+=eset+' '+elp+'='+svarb+envp_l+svare+evs+svarb+elp+svare+'\n' sb+=eset+' '+ellp+'='+svarb+envp_l+svare+evs+svarb+ellp+svare+'\n' # Say that environment is set (to avoid recursion) sb+=eset+' '+envps+'=1\n' # Finish environment batch file if wb=='yes': sb+='\n' sb+='exit /b 0\n' # Check if save to bat file ***************************************************************************************** bf=i.get('bat_file', '') pnew='' if bf=='': bf=cfg['default_bat_name']+hosd.get('script_ext','') # Preparing to add or update entry xx='added' ltags=sorted(ltags) dd={'tags':ltags, 'setup':setup, 'env':env, 'deps':deps, 'soft_uoa':duid, 'customize':cus, 'env_script':bf} if duid!='': dd['soft_uoa']=duid pduoa=i.get('package_uoa','') if pduoa!='': dd['package_uoa']=pduoa # Probably should have come from pi, but there are too many sources of pi ! # install_location = i.get('install_path', i.get('full_path_install', '')) dd['install_location'] = install_location ii={'action':'add', 'module_uoa':cfg['module_deps']['env'], 'dict':dd, 'sort_keys':'yes', 'substitute':'yes'} if enduoa!='': ii['data_uoa']=enduoa if enruoa!='': ii['repo_uoa']=enruoa if update: ii['action']='update' xx='updated' # Adding/updating if dname!='': ii['data_name']=dname rx=ck.access(ii) if rx['return']>0: return rx enduoa=rx['data_uoa'] enduid=rx['data_uid'] pnew=rx['path'] if o=='con': ck.out('') ck.out('Environment entry '+xx+' ('+enduoa+')!') # Record batch file if pnew=='': pb=bf else: pb=os.path.join(pnew, bf) # Write file rx=ck.save_text_file({'text_file':pb, 'string':sb}) if rx['return']>0: return rx return {'return':0, 'env_data_uoa':enduoa, 'env_data_uid':enduid, 'deps':deps} ############################################################################## # search tool in pre-defined paths def search_tool(i): """ Input: { path_list - path list file_name - name of file to find (can be with patterns) (recursion_level_max) - if >0, limit dir recursion (can_be_dir) - if 'yes', return matched directories as well (return_symlinks) - if 'yes', symlinks are returned as-is. Otherwise, they're resolved } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 list - list of file (see ck.list_all_files) elapsed_time - elapsed time } """ o=i.get('out','') import time import os start_time = time.time() pl=i['path_list'] fn=i['file_name'] pt='' rlm=i.get('recursion_level_max',0) cbd=i.get('can_be_dir','') return_symlinks = i.get('return_symlinks','') if fn.find('?')>=0 or fn.find('*')>=0: pt=fn fn='' lst=[] for p in pl: if o=='con': ck.out(' * Searching in '+p+' ...') r=list_all_files({'path':p, 'file_name':fn, 'pattern':pt, 'can_be_dir':cbd, 'recursion_level_max':rlm}) if r['return']>0: return r lst.extend( r['list'] ) # if return_symlinks != 'yes': # # resolving symlinks # lst = [os.path.realpath(p) for p in lst] # #removing duplicates # recorded_paths = set() # record_path = recorded_paths.add # lst = [p for p in lst if not (p in recorded_paths or record_path(p))] elapsed_time = time.time() - start_time return {'return':0, 'list':lst, 'elapsed_time':elapsed_time} ############################################################################## # A helper function for list_all_files() def _internal_check_encoded_path_is_dir( path ): """ Need this complex structure to support UTF-8 file names in Python 2.7 """ import os import sys try: if os.path.isdir( path ): return path except Exception as e: try: path = path.encode('utf-8') if os.path.isdir( path ): return path except Exception as e: try: path = path.encode(sys.stdin.encoding) if os.path.isdir(p): return path except Exception as e: pass return None ############################################################################## # List all files recursively in a given directory def list_all_files(i): """ Input: { path - top level path (file_name) - search for a specific file name (pattern) - return only files with this pattern (path_ext) - path extension (needed for recursion) (can_be_dir) - if 'yes', return matched directories as well (recursion_level_max) - if >0, limit dir recursion } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 list - list of found files } """ import sys import os list_of_results=[] fname = i.get('file_name', '') fname_with_sep_bool = fname.find(os.sep)>=0 can_be_dir = i.get('can_be_dir', '') can_be_dir_bool = can_be_dir == 'yes' pattern=i.get('pattern','') if pattern!='': import fnmatch pe = i.get('path_ext', '') po = i.get('path', '') if sys.version_info[0]<3: po=unicode(po) rl=i.get('recursion_level',0) rlm=i.get('recursion_level_max',0) if rl>rlm: return {'return':0, 'list':[]} try: dirList=os.listdir(po) except Exception as e: pass else: for fn in dirList: p='' try: p=os.path.join(po, fn) except Exception as e: pass if p!='': candidate = None if fname!='': if fname_with_sep_bool and os.path.isdir(p): deep_candidate = os.path.join(po, fname) if os.path.exists( deep_candidate ): candidate = deep_candidate elif fname==fn: candidate = p elif pattern!='' and fnmatch.fnmatch(fn, pattern): candidate = p if candidate and (candidate not in list_of_results): if os.path.isfile( candidate ) or (can_be_dir_bool and os.path.isdir( candidate )): list_of_results.append( candidate ) if _internal_check_encoded_path_is_dir(p): r=list_all_files({'path':p, 'path_ext':os.path.join(pe, fn), 'pattern':pattern, 'file_name':fname, 'can_be_dir':can_be_dir, 'recursion_level':rl+1, 'recursion_level_max':rlm}) if r['return']>0: return r list_of_results.extend( r.get('list',[]) ) return {'return':0, 'list':list_of_results} ############################################################################## # detect is given software is already installed and register it in the CK or install it if package exists def check(i): """ Input: { (target) - if specified, use info from 'machine' module or (host_os) - host OS (detect, if omitted) (target_os) - target OS (detect, if omitted) (target_device_id) - target device ID (detect, if omitted) (data_uoa) or (uoa) - software UOA entry or (tags) - search UOA by tags (separated by comma) (interactive) - if 'yes', and has questions, ask user (quiet) - if 'yes', do not ask questions but select default value (default_selection) - default value for the selection from the menu (first_match) - in case of match ambiguity in menu selection, just take the first match (skip_help) - if 'yes', skip print help if not detected (when called from env setup) (deps) - already resolved deps (if called from env) (dep_add_tags.{KEY}) - extra tags added to specific subdictionary of deps{} for this particular resolution session (extra_version) - add extra version, when registering software (for example, -trunk-20160421) Be careful - if there is auto version detection, CK will say that version has changed and will try to remove entry! (extra_tags) - add extra tags to separate created entry from others (for example Python 2.7 vs Anaconda Python 2.7) (extra_name) - add extra name to soft (such as anaconda) (force_env_data_uoa) - force which env UID to use when regstering detect software - useful when reinstalling broken env entry to avoid breaking all dependencies of other software ... (search_dirs) - extra directories where to search soft (string separated by comma) (search_dir) - search only in this directory (useful for Spack and EasyBuild) (search_depth) - force directory recursive search depth when detecting installed software (soft_name) - name to search explicitly (version_from) - check version starting from ... (string or list of numbers) (version_to) - check version up to ... (string list of numbers) (full_path) - force full path (rather than searching in all directories) } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 path_install - path to the detected software cus - dict with filled in info for the software } """ import os import json import copy o=i.get('out','') oo='' if o=='con': oo=o # Check if target if i.get('target','')!='': r=ck.access({'action':'init', 'module_uoa':cfg['module_deps']['machine'], 'input':i}) if r['return']>0: return r device_cfg=i.get('device_cfg',{}) # Check host/target OS/CPU hos=i.get('host_os','') tos=i.get('target_os','') tdid=i.get('target_device_id','') r=ck.access({'action':'detect', 'module_uoa':cfg['module_deps']['platform.os'], 'host_os':hos, 'target_os':tos, 'target_device_id':tdid, 'skip_info_collection':'yes'}) if r['return']>0: return r hos=r['host_os_uid'] hosx=r['host_os_uoa'] hosd=r['host_os_dict'] tos=r['os_uid'] tosx=r['os_uoa'] tosd=r['os_dict'] tosd.update(device_cfg.get('update_target_os_dict',{})) tbits=tosd.get('bits','') hplat=hosd.get('ck_name','') tplat=tosd.get('ck_name','') # Check versions vfrom=i.get('version_from',[]) vto=i.get('version_to',[]) if type(vfrom)!=list: rx=split_version({'version':vfrom}) if rx['return']>0: return rx vfrom=rx['version_split'] if type(vto)!=list: rx=split_version({'version':vto}) if rx['return']>0: return rx vto=rx['version_split'] tags=i.get('tags','') # Check soft UOA duoa=i.get('uoa', '') if duoa=='': duoa=i.get('data_uoa','') requested_muoa=i.get('module_uoa','') if duoa=='' and requested_muoa=='': # Try to detect CID in current path rx=ck.detect_cid_in_current_path({}) if rx['return']==0: duoa=rx.get('data_uoa','') if tags: # if tags are available, try searching both in tags and variations r=ck.access({'action': 'search_in_variations', 'data_uoa': duoa, 'module_uoa': 'misc', 'query_module_uoa': work['self_module_uid'], 'tags': tags, }) if r['return']>0: return r l=r['lst'] if len(l)>1: # FIXME: we could be smarter and assume several soft_candidates from the very start, # merging all the options found into one big selector. ck.out("Found {} soft candidate entries matching tags '{}' :".format(len(l), tags)) for candidate in l: candidate_tags = candidate['meta']['tags'] required_variations = candidate['required_variations'] ck.out("\tck detect soft:{:<42} # --tags={}".format(candidate['data_uoa'], ','.join(candidate_tags+required_variations)) ) return {'return':1, 'error': "Please use a command that uniquely defines a soft: entry"} elif len(l)==1: r=l[0] soft_entry_dict=r['meta'] required_variations=r['required_variations'] else: return {'return':1, 'error':'software entry was not found'} elif duoa: # if tags were not available, try to load directly r=ck.access({'action':'load', 'module_uoa':work['self_module_uid'], 'data_uoa':duoa, }) if r['return']>0: return r soft_entry_dict=r['dict'] required_variations = [] else: return {'return':1, 'error':'please define either --data_uoa or --tags or both to get going'} duoa=r['data_uoa'] duid=r['data_uid'] soft_entry_path=r['path'] cus=soft_entry_dict.get('customize',{}) ######################################################################## # Check env from input envx=copy.deepcopy(i.get('env',{})) ienv=copy.deepcopy(i.get('install_env',i.get('ienv',{}))) # parse install_env overrides out of install_env{}, install_env.XXX and ienv.XXX CLI options for q in i: if q.startswith('env.'): envx[q[len('env.'):]]=i[q] elif q.startswith('ienv.'): ienv[q[len('ienv.'):]]=i[q] elif q.startswith('install_env.'): ienv[q[len('install_env.'):]]=i[q] supported_variations = soft_entry_dict.get('variations', {}) missing_variations = set(required_variations) - set(supported_variations) if missing_variations: return {'return':1, 'error':'Variations {} are not supported by soft:{}'.format(missing_variations, duoa)} # Update this cus from all the supported variations. # Detect if an incompatible mix of variation tags was required # that would lead to undefined behaviour, and bail out if so. # if required_variations: extra_env_from_variations = {} extra_cus_from_variations = {} for req_variation in required_variations: extra_env = supported_variations[req_variation].get('extra_env',{}) colliding_vars = set(extra_env_from_variations.keys()) & set(extra_env.keys()) # non-empty intersection means undefined behaviour for coll_var in colliding_vars: # have to check actual values to detect a mismatch if extra_env_from_variations[coll_var] != extra_env[coll_var]: return { 'return':1, 'error':'contradiction on variable ({}) detected when adding "{}" variation tag'.format(coll_var,req_variation)} extra_cus = supported_variations[req_variation].get('extra_customize',{}) colliding_cuss = set(extra_cus_from_variations.keys()) & set(extra_cus.keys()) # non-empty intersection means undefined behaviour for coll_cus in colliding_cuss: # have to check actual values to detect a mismatch if extra_cus_from_variations[coll_cus] != extra_env[coll_cus]: return { 'return':1, 'error':'contradiction on customize ({}) detected when adding "{}" variation tag'.format(coll_cus,req_variation)} extra_env_from_variations.update( extra_env ) # merge of one particular variation extra_cus_from_variations.update( extra_cus ) ienv.update( extra_env_from_variations ) # merge of all variations cus.update( extra_cus_from_variations ) extra_version=i.get('extra_version', cus.get('extra_version','')) # Check if restricts dependency to a given host or target OS rx=check_target({'dict':cus, 'host_os_uoa':hosx, 'host_os_dict':hosd, 'target_os_uoa':tosx, 'target_os_dict':tosd}) if rx['return']>0: return rx # Check if need to resolve dependencies deps=i.get('deps',{}) dep_add_tags = i.get('dep_add_tags', {}) for q in i: if q.startswith('deps.'): preset_deps[q[5:]]=i[q].split(':')[-1] elif q.startswith('dep_add_tags.'): _ , dep_name = q.split('.') dep_add_tags[dep_name] = i[q] sbat='' if len(deps)==0: deps=soft_entry_dict.get('deps',{}) if len(deps)>0: ii={'action':'resolve', 'module_uoa':cfg['module_deps']['env'], 'host_os':hos, 'target_os':tos, 'target_device_id':tdid, 'deps':deps, # 'env':envx, 'dep_add_tags': dep_add_tags, 'env':copy.deepcopy(envx), 'install_env':ienv, 'out':oo} rx=ck.access(ii) if rx['return']>0: return rx sbat=rx['bat'] # if o=='con': # x=duoa # if duid!=duoa: x+=' ('+duid+')' # ck.out('Software description entry found: '+x) rr={'return':0} # Check if customize script is redirected into another entry: # another_entry_with_customize_script=soft_entry_dict.get('use_customize_script_from_another_entry', None) if another_entry_with_customize_script: r=ck.access({'action':'find', 'module_uoa': another_entry_with_customize_script.get('module_uoa', work['self_module_uid']), 'data_uoa': another_entry_with_customize_script.get('data_uoa','') }) if r['return']>0: return r customization_script_path = r['path'] else: customization_script_path = soft_entry_path cs=None rx=ck.load_module_from_path({'path':customization_script_path, 'module_code_name':cfg['custom_script_name'], 'skip_init':'yes'}) if rx['return']==0: cs=rx['code'] elif another_entry_with_customize_script or not rx['error'].startswith("can't find module code"): return rx soft_version_cmd=cus.get('soft_version_cmd',{}).get(hplat,'') # Decide where to search depending on the Operating System # # 1) list all the potential places: # dir_candidates = [] extra_search_dirs_universal = cus.get('extra_search_dirs_universal', []) dir_candidates.extend( extra_search_dirs_universal ) extra_search_dirs = cus.get('extra_search_dirs', {}).get(hplat, []) dir_candidates.extend( extra_search_dirs ) if cus.get('detect_in_soft_dir_only', '')=='yes': dir_candidates.append( soft_entry_path ) else: if hplat=='win': dir_candidates.extend([ os.environ.get('ProgramW6432', ''), os.environ.get('ProgramFiles(x86)', ''), os.environ.get('ProgramFiles', ''), 'C:\\Program Files', 'D:\\Program Files', 'C:\\Program Files (x86)', 'D:\\Program Files (x86)', ]) else: dir_candidates.extend([ '/usr', '/opt', ]) if hosd.get('macos'): # MacOSX is currently treated as a flavour of Linux dir_candidates.extend([ '/usr/local/Cellar', # The location of software installed by brew prior to softlinking ]) dir_candidates.append( os.environ.get(env_install_path, '') # from CK_TOOLS env ) dir_separator = ';' if hplat=='win' else ':' dir_candidates.extend( os.environ.get(env_search, '').split( dir_separator ) # from CK_DIRS ) dir_candidates.extend( i.get('search_dirs', '').split(',') # from input ) from os.path import expanduser dir_candidates.append( expanduser("~") # from user space ) if cus.get('detect_in_soft_dir', '')=='yes': dir_candidates.append( soft_entry_path ) # # 2) filter through the candidates to find suitable and unique ones: # sdirs=hosd.get('dir_sep','') dirs=[] for candidate in dir_candidates: if candidate: candidate=candidate.replace('$#sep#$', sdirs) if os.path.isdir(candidate) and candidate not in dirs: dirs.append(candidate) # Check if interactive iv='yes' quiet=i.get('quiet','') if quiet=='yes' or o!='con': iv='' # If there is a function to customize dirs, call it if 'dirs' in dir(cs): ii={"host_os_uoa":hosx, "host_os_uid":hos, "host_os_dict":hosd, "target_os_uoa":tosx, "target_os_uid":tos, "target_os_dict":tosd, "target_device_id":tdid, "soft_entry_path":soft_entry_path, # This is also a valid path suitable for soft detection (but not by default) "cfg":soft_entry_dict, "install_env":ienv, "self_cfg":cfg, "ck_kernel":ck, "dirs":dirs, "interactive":iv } rx=cs.dirs(ii) if rx['return']>0: return rx if rx.get('dirs'): dirs=rx['dirs'] # If anything was returned at all, trust that value, even if it is empty. # NB: Otherwise it may be the case that the original list was modified as a side effect. # Check if substitute all search dirs via kernel! x=ck.cfg.get('soft_search_dirs','') if x!='': dirs=x.split(',') # Check if substitute by 1 dir if i.get('search_dir','')!='': dirs=[i['search_dir'].strip()] # Check which file to search for sname=i.get('soft_name','') if sname=='': ry=prepare_target_name({'host_os_dict':hosd, 'target_os_dict':tosd, 'cus':cus}) if ry['return']>0: return ry sname=ry['tool'] cbd=cus.get('soft_can_be_dir','') sen=cus.get('soft_extra_name','') osname=sname if sname=='': return {'return':1, 'error':'software description doesn\'t have a name of file to search ...'} # Check if search for extensions gcc-4.9, clang-3.8, etc if hplat=='linux' and cus.get('search_numeric_ext_on_linux','')=='yes': sname+='*' # Search tools suname=soft_entry_dict.get('soft_name','') x=sname if suname!='': x=suname+' ('+sname+')' ck.out('') ck.out(' Searching for '+x+' to automatically register in the CK - it may take some time, please wait ...') ck.out('') # Check directory recursion depth customization when searching for installed software xrlm=i.get('search_depth','') if xrlm=='': xrlm=ck.cfg.get('force_soft_search_depth','') if xrlm=='': xrlm=cus.get('limit_recursion_dir_search_all','') if xrlm!='': rlm=int(xrlm) else: rlm=cus.get('limit_recursion_dir_search',{}).get(hplat,0) skip_sort='no' fp=i.get('full_path','') if fp!='': lst=[fp] else: rx=search_tool({'path_list':dirs, 'file_name':sname, 'recursion_level_max':rlm, 'can_be_dir':cbd, 'out':'con'}) if rx['return']>0: return rx lst=rx['list'] et=rx['elapsed_time'] # Limit to required ones if 'limit' in dir(cs): rx=cs.limit({'list':lst, 'host_os_dict':hosd, 'target_os_dict':tosd, 'soft_name':osname, 'ck_kernel':ck}) if rx['return']>0: return rx lst=rx['list'] if rx.get('skip_sort','')!='': skip_sort=rx['skip_sort'] # Print results # if o=='con': ck.out('') ck.out(' Search completed in '+('%.1f' % et)+' secs. Found '+str(len(lst))+' target files (may be pruned) ...') # Select, if found il=0 if len(lst)>0: # Trying to detect version if o=='con': ck.out('') ck.out(' Detecting and sorting versions (ignore some work output) ...') vlst=[] # FGG: decided to remove after discussing with LLNL because often picks up strange path instead of correct one ... # # Sometimes can be the same paths (due to soft links) - remove: # lst1=[] # lst2=[] # for q in reversed(lst): # q2=os.path.realpath(q) # if q2 not in lst2: # lst1.append(q) # lst2.append(q2) # lst=reversed(lst1) # return to original order if need to avoid sorting # Process each path if o=='con': ck.out('') for q in lst: kk={'path':q} pr='' if o=='con': pr=' * '+q # Try to detect version ver='' sver=[] ii={'full_path':q, 'bat':sbat, 'host_os_dict':hosd, 'target_os_dict':tosd, 'cmd':soft_version_cmd, 'use_locale':cus.get('use_locale_for_version',''), 'customize':cus, 'custom_script_obj':cs, 'data_uid': duid, 'deps': deps } if ck.cfg.get('minimize_soft_detect_output','')!='yes': ii['out']=o rx=get_version(ii) if rx['return']>0: if o=='con': pr+='\n WARNING: '+rx['error'] else: ver=rx['version'] # Split version rx=split_version({'version':ver}) if rx['return']>0: return rx sver=rx['version_split'] kk['version']=ver kk['version_detected']=ver kk['version_split']=sver if o=='con': pr+=' (Version '+ver+')' skip=False if len(vfrom)>0: r=compare_versions({'version1':vfrom, 'version2':sver}) if r['return']>0: return r if r['result']=='>': skip=True if not skip and len(vto)>0: r=compare_versions({'version1':sver, 'version2':vto}) if r['return']>0: return r if r['result']=='>': skip=True if skip: pr+=' - skipped because of version constraints!' if o=='con': ck.out(pr) if not skip and (cus.get('add_only_with_version','')!='yes' or ver!=''): vlst.append(kk) if o=='con': ck.out('') # Sort by version if skip_sort!='yes': vlst=sorted(vlst, key=lambda k: (internal_get_val(k.get('version_split',[]), 0, 0), internal_get_val(k.get('version_split',[]), 1, 0), internal_get_val(k.get('version_split',[]), 2, 0), internal_get_val(k.get('version_split',[]), 3, 0), internal_get_val(k.get('version_split',[]), 4, 0), k.get('path','')), reverse=True) lst = [ q['path'] for q in vlst ] if len(lst)>1: if o=='con': ck.out('') default_selection = i.get('default_selection', '0') if iv=='yes': ck.out(' Registering software installations found on your machine in the CK:') ck.out('') ck.out(' (HINT: enter -1 to force CK package installation)') ck.out('') ver_options = [] for kk in vlst: q=kk['path'] ver=kk.get('version','') ver_options.append( 'Version {} - {}'.format(ver, q) if ver else q ) select_adict = ck.access({'action': 'select_string', 'module_uoa': 'misc', 'options': ver_options, 'default': default_selection, 'question': 'Please select the number of any above installation', 'first_match': i.get('first_match', ''), }) if select_adict['return']>0: return select_adict il = select_adict.get('selected_index', -1) if il<0: return {'return':1, 'error':'selection number is not recognized'} # If not found, quit if len(lst)==0: if i.get('skip_help','')!='yes': r=print_help({'data_uoa':duid, 'platform':hplat}) # Ignore output return {'return':16, 'error':'software was not automatically found on your system! Please, install it and re-try again!'} # Check if CK install dict already exists pf=lst[il] env_data_uoa=i.get('force_env_data_uoa','') puoa='' dname='' xtags='' rx=find_config_file({'full_path':pf, 'data_uid': duid}) if rx['return']>0: return rx found=rx['found'] if found=='yes': dx=rx['dict'] cus=dx.get('customize',{}) extra_version=dx.get('extra_version','') if dx.get('env_data_uoa','')!='' and env_data_uoa=='': env_data_uoa=dx['env_data_uoa'] dname=soft_entry_dict.get('soft_name','') if cus.get('package_extra_name','')!='': dname+=cus['package_extra_name'] puoa=dx.get('package_uoa','') xtags=dx.get('tags','') # FGG: should I add deps here or not - the thing is that the env # most likely changed so probably not ... # New update -> I can now restore UIDs with deps, so maybe it's ok ... if o=='con': ck.out('') ck.out(' Found pre-recorded CK installation info ...') # Merge all relevant tags: extra_tags=i.get('extra_tags','').strip() extra_tags_set = set( extra_tags.split(',') ) if extra_tags else set() main_tags_set = set( xtags.split(',') ) vari_tags_set = set( required_variations ) xtags = ','.join( list( main_tags_set | vari_tags_set | extra_tags_set ) ) en=i.get('extra_name','') if en!='': en=' '+en # Attempt to register in CK if o=='con': ck.out('') ck.out(' Registering in the CK ('+pf+') ...') ck.out('') ii={'data_uoa':duid, 'customize':cus, 'full_path':pf, 'quiet':quiet, 'host_os':hos, 'target_os':tos, 'target_device_id':tdid, 'deps':deps, 'env':envx, 'ienv':ienv, 'env_data_uoa':env_data_uoa, 'soft_name':dname, 'soft_add_name':en, 'package_uoa':puoa, 'extra_version':extra_version, 'tags':xtags, 'out':oo} if cus.get('collect_device_info','')!='yes': ii['skip_device_info_collection']='yes' rz=setup(ii) if rz['return']>0: return rz xeduoa=rz['env_data_uoa'] xeduid=rz['env_data_uid'] if o=='con': ck.out(' Successfully registered with UID: '+xeduid) return rz ############################################################################## # get version of a given software (internal) def get_version(i): """ Input: { full_path bat cmd custom_script_obj host_os_dict (show) - if 'yes', show output file (skip_existing) - if 'yes', force detecting version again (skip_add_target_file) - if 'yes', do not add target file at the beginning of CMD to detect version (use_locale) - if 'yes', use locale to decode output } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 version - string version version_lst - raw output (as list) } """ import os o=i.get('out','') fp=i.get('full_path','') sb=i.get('bat','') soft_version_cmd=i.get('cmd') data_uid = i.get('data_uid') cs=i.get('custom_script_obj', None) cus=i.get('customize',{}) # should we be more strict [] here? hosd=i.get('host_os_dict',{}) tosd=i.get('target_os_dict',{}) bprefix=hosd.get('batch_prefix','') ubtr=hosd.get('use_bash_to_run','') svarb=hosd.get('env_var_start','') svarb1=hosd.get('env_var_extra1','') svare=hosd.get('env_var_stop','') svare1=hosd.get('env_var_extra2','') sexe=hosd.get('set_executable','') sbp=hosd.get('bin_prefix','') envsep=hosd.get('env_separator','') scall=hosd.get('env_call','') sext=hosd.get('script_ext','') eifsc=hosd.get('env_quotes_if_space_in_call','') nout=hosd.get('no_output','') deps=i.get('deps',{}) sb=bprefix+sb ver='' lst=[] cmd='' # Attempt to check via CK config file if i.get('skip_existing','')!='yes': rx=find_config_file({'full_path':fp, 'data_uid': data_uid}) if rx['return']>0: return rx found=rx['found'] if found=='yes': ver=rx['dict'].get('customize',{}).get('version','') if ver=='': # Preparing CMD if 'version_cmd' in dir(cs): rx=cs.version_cmd({'full_path':fp, 'host_os_dict':hosd, 'target_os_dict':tosd, 'cmd':soft_version_cmd, 'ck_kernel':ck, 'customize':cus, 'out':o, 'deps':deps}) if rx['return']>0: return rx cmd=rx.get('cmd','') ver=rx.get('version','') elif soft_version_cmd: if eifsc!='' and fp.find(' ')>=0 and not fp.startswith(eifsc): fp=eifsc+fp+eifsc if o!='con': cmd+=nout if i.get('skip_add_target_file','')=='yes': cmd+=' '+soft_version_cmd else: cmd+=fp+' '+soft_version_cmd if ver=='' and cmd: # Generate tmp file rx=ck.gen_tmp_file({}) if rx['return']>0: return rx ftmp=rx['file_name'] cmd=cmd.replace('$#filename#$', ftmp) if o=='con': cmdz=cmd.replace('\n','\n ') ck.out('') ck.out(' ==============================') ck.out(' Prepared CMD to detect version:') ck.out(' '+cmdz.strip()) # Finalizing batch file sb+='\n'+cmd+'\n' # Record to tmp batch and run rx=ck.gen_tmp_file({'prefix':'tmp-', 'suffix':sext, 'remove_dir':'no'}) if rx['return']>0: return rx fnb=rx['file_name'] rx=ck.save_text_file({'text_file':fnb, 'string':sb}) if rx['return']>0: return rx # Executing script y='' if sexe!='': y+=sexe+' '+fnb+envsep y+=' '+scall+' '+fnb if ubtr!='': y=ubtr.replace('$#cmd#$',y) if o=='con': ck.out('') ck.out(' Executing "'+y+'" ...') ry=os.system(nout+y) # ignore return code (checking output file instead) os.remove(fnb) if os.path.isfile(ftmp): import sys import locale en=sys.stdout.encoding if i.get('use_locale','')=='yes': en=locale.getdefaultlocale()[1] success=True for xen in [en, sys.stdout.encoding, locale.getdefaultlocale()[1]]: try: rx=ck.load_text_file({'text_file':ftmp, 'split_to_list':'yes', 'encoding':en}) except UnicodeDecodeError: success=False if success: break if success: if rx['return']>0: return rx lst=rx['lst'] if os.path.isfile(ftmp): os.remove(ftmp) if len(lst)==0: return {'return':16, 'error':'version output file is empty'} if i.get('show','')=='yes': ck.out(' Output:') ck.out('') for q in lst: ck.out(' '+q) if 'parse_version' in dir(cs): # Calling customized script to parse version ii={'output':lst, 'host_os_dict':hosd, 'full_path':fp, 'ck_kernel':ck} rx=cs.parse_version(ii) if rx['return']>0 and rx['return']!=16: return rx ver=rx.get('version','') else: ck.out('parse_version() method is not defined => attempting to parse it trivially') ver = lst[0].strip() if ver=='': return {'return':16, 'error':'version was not detected'} if o=='con': ck.out('') ck.out(' Version detected: '+ver) ck.out('') return {'return':0, 'version':ver, 'version_lst':lst} ############################################################################## # internal function: get value from list without error if out of bounds def internal_get_val(lst, index, default_value): v=default_value if index 0, if error (error) - error text if return > 0 } """ import os duoa=i['data_uoa'] hplat=i['platform'] ti='' # If only one related software entry found, try to read text notes from it rx=ck.access({'action':'find', 'module_uoa':work['self_module_uid'], 'data_uoa':duoa}) if rx['return']>0: return rx pppx=rx['path'] ppx=os.path.join(pppx,'install.txt') if os.path.isfile(ppx): rx=ck.load_text_file({'text_file':ppx}) if rx['return']==0: ti+=rx['string'] ppx=os.path.join(pppx,'install.'+hplat+'.txt') if os.path.isfile(ppx): rx=ck.load_text_file({'text_file':ppx}) if rx['return']==0: if ti!='': ti+='\n' ti+=rx['string'] if ti!='': read=True ck.out('****** Installation notes: ******') ck.out(ti) ck.out('*********************************') else: # Show possible Wiki page rx=ck.inp({'text':' Would you like to open wiki pages about installation and other info (if exists) (Y/n): '}) x=rx['string'].strip().lower() if x!='n' and x!='no': ck.out('') rx=ck.access({'action':'wiki', 'module_uoa':work['self_module_uid'], 'data_uoa':duoa}) if rx['return']>0: return rx ck.out('') return {'return':0} ############################################################################## # check that host and target OS is supported def check_target(i): """ Input: { dict - dictionary with info about supported host and target OS host_os_uoa - host OS UOA (already resolved) host_os_dict - host OS dict (already resolved) target_os_uoa - target OS UOA (already resolved) target_os_dict - target OS UOA (already resolved) } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ cus=i['dict'] hosx=i['host_os_uoa'] hosd=i['host_os_dict'] tosx=i['target_os_uoa'] tosd=i['target_os_dict'] # Check if restricts dependency to a given host or target OS only_hos=cus.get('only_for_host_os',[]) if len(only_hos)>0: if hosx not in only_hos: return {'return':1, 'error':'host OS is not supported by this software'} only_hos1=cus.get('only_for_host_os_tags',[]) if len(only_hos1)>0: x=hosd.get('tags',[]) found=False for xx in only_hos1: if xx in x: found=True break if not found: return {'return':1, 'error':'host OS family is not supported by this software'} only_tos=cus.get('only_for_target_os',[]) if len(only_tos)>0: if tosx not in only_tos: return {'return':1, 'error':'target OS is not supported by this software'} only_tos1=cus.get('only_for_target_os_tags',[]) if len(only_tos1)>0: x=tosd.get('tags',[]) found=False for xx in only_tos1: if xx in x: found=True break if not found: return {'return':1, 'error':'target OS family is not supported by this software'} return {'return':0} ############################################################################## # split version def split_version(i): """ Input: { version - string version } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 version_split - split version } """ import re ver=i['version'] # Split version sver=[] if ver!='': if ver!='': sver1=re.split('\.|\-|\_', ver) for q in sver1: x=q try: x=int(q) except: #pass - causes problems when mixing strings and ints ... x=0 sver.append(x) return {'return':0, 'version_split':sver} ############################################################################## # show available software descriptions def show(i): """ Input: { (the same as list; can use wildcards) (out_file) - output to file (for mediawiki) } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ import os import copy o=i.get('out','') of=i.get('out_file','') if of!='': xof=os.path.splitext(of) html=False if o=='html' or i.get('web','')=='yes': html=True h='' h2='' if i.get('new','')=='yes': ii=copy.deepcopy(i) ii['action']='preload_html_for_lists' ii['module_uoa']=cfg['module_deps']['misc'] ii['ck_title']='Shared CK software detection plugins' r=ck.access(ii) if r['return']>0: return r h=r['html_start']+'\n' h2=r['html_stop']+'\n' unique_repo=False if i.get('repo_uoa','')!='': unique_repo=True ii=copy.deepcopy(i) ii['out']='' ii['action']='list' ii['add_meta']='yes' ii['time_out']=-1 rx=ck.access(ii) if rx['return']>0: return rx ll=sorted(rx['lst'], key=lambda k: k['data_uoa']) if html: h+='

Please check our new beta browser for CK components!

\n' h+='
\n' h+='You can detect installed software and register it in the CK as follows:\n' h+='
\n'
       h+=' ck pull repo:{Repo UOA - see below}\n'
       h+=' ck detect soft:{Soft UOA - see below}\n'
       h+='
\n' h+='using tags:\n' h+='
\n'
       h+=' ck detect soft --tags={some tags from below}\n'
       h+='
\n' h+='in an unusual path:\n' h+='
\n'
       h+=' ck detect soft:{Soft UOA - see below} --search_dirs={path to this software}\n'
       h+='
\n' h+='or for a different OS target (Android):\n' h+='
\n'
       h+=' ck ls os:android* | sort\n'
       h+=' ck detect soft:{Soft UOA - see below} --target_os={OS UOA from above}\n'
       h+='
\n' h+='You can see or use registered virtual CK environments as follows:\n' h+='
\n'
       h+=' ck show env\n'
       h+=' ck show env --tags={some tags from below}\n'
       h+='\n'
       h+=' ck virtual env:{UID from above}\n'
       h+=' ck virtual env --tags={some tags from below}\n'
       h+='
\n' h+='

\n' h+='See

ck detect soft --help
for more detection options.\n' h+='See related CK packages,\n' h+=' CK documentation,\n' h+=' "how to contribute" guide,\n' h+=' ACM ReQuEST-ASPLOS\'18 report\n' h+=' and the latest CK paper for further details.\n' h+='

\n' h+='\n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' repo_url={} repo_private={} size=0 isize=1 private='' num=0 for l in ll: ln=l['data_uoa'] lr=l['repo_uoa'] lr_uid=l['repo_uid'] url='' if lr=='default': url='' #'http://github.com/ctuning/ck' elif lr_uid in repo_url: url=repo_url[lr_uid] else: rx=ck.load_repo_info_from_cache({'repo_uoa':lr_uid}) if rx['return']>0: return rx url=rx.get('dict',{}).get('url','') repo_private[lr_uid]=rx.get('dict',{}).get('private','') repo_url[lr_uid]=url private=repo_private.get(lr_uid,'') if lr not in cfg.get('skip_repos',[]) and private!='yes' and url!='': num+=1 lm=l['meta'] ld=lm.get('desc','') soft_name=lm.get('soft_name','') cus=lm.get('customize',{}) ad=lm.get('auto_detect','') if ad!='yes': ad='no' ep=cus.get('env_prefix','') xhos=cus.get('only_for_host_os_tags',[]) xtos=cus.get('only_for_target_os_tags',[]) tmpl=lm.get('template','') template=lm.get('template_type','') if tmpl=='yes' and template=='': template='yes' tags=lm.get('tags',[]) ytags=','.join(tags) yhos='' ytos='' for q in xhos: if yhos!='': yhos+=',' yhos+=q for q in xtos: if ytos!='': ytos+=',' ytos+=q if yhos=='': yhos='any' else: yhos=yhos.replace('linux','linux,macos') if ytos=='': ytos='any' else: ytos=ytos.replace('linux','linux,macos') if lr=='default': to_get='' elif url.find('github.com/ctuning/')>0: to_get='ck pull repo:'+lr else: to_get='ck pull repo --url='+url x=lr y='' yh='' if url!='': url2=url if url2.endswith('.git'): url2=url2[:-4] yh=url2+'/tree/master/soft/'+ln x='['+url2+' '+lr+']' y='['+yh+' link]' ############################################################### if html: h+=' \n' x1='' x2='' z1='' if url!='': x1='' x2='' z1='' z11='' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' h+=' \n' ############################################################### elif o=='mediawiki': s='' s+='\n' s+='=== '+ln+' ('+soft_name+') ===\n' s+='\n' s+='Auto-detect?: '+ad+'\n' s+='
Environment variable: '+ep+'\n' s+='\n' s+='Tags: '+ytags+'\n' s+='
Host OS tags: '+yhos+'\n' s+='
Target OS tags: '+ytos+'\n' if y!='': s+='\n' s+='Software entry with meta: '+y+'\n' s+='\n' s+='Which CK repo: '+x+'\n' if to_get!='': s+='
How to get: '+to_get+'\n' if to_get!='': s+='\n' s+='How to detect: ck detect soft:'+ln+' (--target_os={CK OS UOA})\n' s+='\n' if of=='': ck.out(s) else: with open(of, "a") as ff: ff.write(s) size+=len(s) if size>=80000: isize+=1 of=xof[0]+str(isize)+xof[1] size=0 ############################################################### elif o=='con' or o=='txt': if unique_repo: ck.out('') s=ln+' - '+ld else: ss='' if len(ln)<35: ss=' '*(35-len(ln)) ss1='' if len(lr)<30: ss1=' '*(30-len(lr)) s=ln+ss+' ('+lr+')' if ld!='': s+=ss1+' '+ld ck.out(s) ck.out('') ck.out(' Total soft detection plugins: '+str(num)) ck.out('') if html: h+='
#Soft UOATemplate?Repo UOATagsHost OSTarget OSNotes
'+str(num)+''+z1+ln+x2+' ('+z11+'CK meta'+x2+')'+template+''+x1+lr+x2+''+ytags+'\n' h+=' '+yhos+'\n' h+=' '+ytos+'\n' h1='Auto-detect? '+ad+'
\n' h1+='Environment variable: '+ep+'
\n' if ld!='': h1+='

\n'+ld h+='

'+h1+'\n' h+='
\n' h+=h2 if of!='': r=ck.save_text_file({'text_file':of, 'string':h}) if r['return']>0: return r return {'return':0, 'html':h} ############################################################################## # get version of a given software (internal) def find_config_file(i): """ Input: { full_path - where to start search } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 found - 'yes' if found dict - loaded dict with the configuration ... filename - filename path - path } """ import os pf=i['full_path'] filter_data_uid = i.get('data_uid', '') pf1=os.path.dirname(pf) found='no' d={} fn='' pf2='' while pf1!=pf and pf1!='': fn=cfg['ck_install_file'] pf2=os.path.join(pf1,fn) if os.path.isfile(pf2): rx=ck.load_json_file({'json_file':pf2}) if rx['return']==0: found='yes' d=rx['dict'] break else: fn=cfg['ck_install_file_saved'] pf2=os.path.join(pf1,fn) if os.path.isfile(pf2): rx=ck.load_json_file({'json_file':pf2}) if rx['return']==0: found='yes' d=rx['dict'] break pf=pf1 pf1=os.path.dirname(pf) config_data_uid = d.get('data_uoa', '') if filter_data_uid and (config_data_uid != filter_data_uid): found = 'no' d = {} return {'return':0, 'found':found, 'dict':d, 'filename':fn, 'path':pf2} ############################################################################## # compare two versions (in list) def compare_versions(i): """ Input: { version1 - version 1 to compare against version2 (list such as [1,62]) version2 - (list such as [1,63]) } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 result "<" - version 1 < version 2 "=" - version 1 == version 2 ">" - version 1 > version 2 } """ def compare_versions_core(v1_orig, v2_orig): len_diff = len(v2_orig)-len(v1_orig) # determine the (signed) length of zero-padding # now pad the shorter to match the longer: (v1, v2) = (v1_orig + [0]*len_diff, v2_orig) if len_diff>0 else (v1_orig, v2_orig + [0]*-len_diff) for j in range(0,len(v1)): (t1, t2) = (type(v1[j]), type(v2[j])) if t1 == t2: # perform natural comparison within the same type if v1[j]v2[j]: return '>' elif t1 == int: # but any integer is higher than any letter combination return '>' elif t2 == int: return '<' return '=' result = compare_versions_core(i['version1'], i['version2']) if i.get('out','')=='con': ck.out(result) return {'return':0, 'result':result} ############################################################################## # compare two versions (in list) def prepare_target_name(i): """ Input: { host_os_dict - host OS dict target_os_dict - target OS dict cus - custom meta } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 tool - tool name } """ cus=i['cus'] hosd=i['host_os_dict'] tosd=i['target_os_dict'] hplat=hosd['ck_name'] tplat=tosd['ck_name'] tool='' sdirs=hosd.get('dir_sep','') plat=tplat osd=tosd if cus.get('soft_file_from_host_os','')=='yes': plat=hplat osd=hosd tool=cus.get('soft_file_universal','') if tool=='': tool=cus.get('soft_file',{}).get(plat,'') file_extensions=hosd.get('file_extensions',{}) # Check file extensions from OS (for example, to specialize .dylib vs .so for MacOS) for k in file_extensions: v=file_extensions[k] tool=tool.replace('$#file_ext_'+k+'#$',v) tool=tool.replace('$#sep#$', sdirs) return {'return':0, 'tool':tool} ############################################################################## # add software detection plugin with template def add(i): """ Input: { (template) - if !='', use this program as template! (tags) - if !='', use these tags } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 } """ o=i.get('out','') # Redirect to a universal template ... muoa=i['module_uoa'] i['original_module_uoa']=muoa i['module_uoa']=cfg['module_deps']['misc'] i['action']='prepare_entry_template' if 'cid' in i: del(i['cid']) r=ck.access(i) if r['return']>0: return r # Update newly created entry with special keys duid=r['data_uid'] duoa=r['data_uoa'] ruid=r['repo_uid'] dd=r['dict'] if 'template' in dd: del(dd['template']) if 'template_type' in dd: del(dd['template_type']) xtags=i.get('tags','') if xtags=='': ck.out('') r=ck.inp({'text':'Enter tags for your new soft detection plugin separated by comma (for example lib,tflite): '}) xtags=r['string'].strip() tags=[] if xtags!='': for q in xtags.split(','): q=q.strip() if q not in tags: tags.append(q) else: for k in dd.get('tags',[]): if k!='template': tags.append(k) dd['tags']=tags ii={'action':'update', 'module_uoa':muoa, 'data_uoa':duid, 'repo_uoa':ruid, 'dict':dd, 'substitute':'yes', 'sort_keys':'yes', 'ignore_update':'yes' } if o=='con': ck.out('') ck.out('Further details about how to update meta.json and customize.py of your new software detection plugin:') ck.out('') ck.out(' * https://github.com/ctuning/ck/wiki/Adding-new-workflows') return ck.access(ii) ############################################################################## # search for soft version in some files def search_version(i): """ Input: { path - path to file (where to start search) } Output: { return - return code = 0, if successful > 0, if error (error) - error text if return > 0 (version) - version string if detected } """ import os o=i.get('out','') ver='' p0=i['path'] f=os.path.basename(p0) p=os.path.dirname(p0) # Searching for spack info while True: p1=os.path.join(p, '.spack/spec.yaml') if os.path.isfile(p1): # Read file and search for version r=ck.load_text_file({'text_file':p1}) if r['return']==0: s=r['string'] # Find first version j=s.find('version: ') if j>=0: j1=s.find('\n',j) if j1>=0: ver=s[j+9:j1].strip() break pp=os.path.dirname(p) if pp==p: break p=pp # If not found, search for .settings if ver=='' and f!='' and f!='/' and f!='\\': f1=os.path.splitext(f)[0]+'.settings' p=os.path.dirname(p0) p1=os.path.join(p,f1) if os.path.isfile(p1): # Read file and search for version r=ck.load_text_file({'text_file':p1}) if r['return']==0: s=r['string'] # Find first version j=s.lower().find(' version:') if j>=0: j1=s.find('\n',j) if j1>=0: ver=s[j+9:j1].strip() return {'return':0, 'version':ver}