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

    ########################################################################
    # Check if force version
    x=i.get('force_version','').strip()
    if x!='': ver=i['force_version'] 

    ########################################################################
    # 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':