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)

              (force_version)      - if !='', use this version

              (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,