<gh_stars>1000+
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
"""
Usage:

def options(opt):
    opt.load('module_info')

$ waf info
"""
# System Imports
import json
import os
from collections import OrderedDict

# waflib imports
from waflib import Build, Errors, Logs, Node, TaskGen, Utils


def to_list(value):
    if isinstance(value, set):
        return list(value)
    elif isinstance(value, list):
        return value[:]
    else:
        return [value]

class ModuleInfoContext(Build.BuildContext):
    cmd = 'info'
    is_project_generator = True

    def execute(self):
        # restore the environments
        self.restore()
        if not self.all_envs:
            self.load_envs()

        self.load_user_settings()
        Logs.info("[WAF] Executing 'info' in '{}'".format(self.variant_dir))
        self.recurse([ self.run_dir ])

        output_dir_node = self.path.make_node(['BinTemp', 'module_info'])
        output_dir_node.mkdir()
        output_dir = output_dir_node.abspath()

        third_party_artifacts = []
        for group in self.groups:
            for task_generator in group:
                if not isinstance(task_generator, TaskGen.task_gen):
                    continue

                target_name = task_generator.target or task_generator.name

                if self.options.targets and target_name not in self.options.targets:
                    continue

                task_generator.post()

                path = task_generator.path.abspath()
                third_party_artifacts += getattr(task_generator.env, 'THIRD_PARTY_USELIBS', [])

                includes = OrderedDict()

                # Module includes
                for entry in to_list(getattr(task_generator, 'includes', [])):
                    if isinstance(entry, Node.Node):
                        entry = entry.abspath()

                    if not os.path.isabs(entry):
                        entry = os.path.join(path, entry)

                    entry = os.path.normpath(entry)
                    if ('\\bintemp\\' not in entry.lower()) and ('/bintemp/' not in entry.lower()): # Ignore all generated stuff!
                        includes[entry] = None

                # 3rdParty module includes
                dependencies = to_list(getattr(task_generator, 'use', [])) + to_list(getattr(task_generator, 'uselib', []))
                for dependency in sorted(set(dependencies)):
                    includes_key = 'INCLUDES_{}'.format(dependency)

                    for env in self.all_envs:
                        for entry in self.all_envs[env][includes_key]:
                            if isinstance(entry, Node.Node):
                                entry = entry.abspath()

                            entry = os.path.normpath(entry)
                            if ('\\bintemp\\' not in entry.lower()) and ('/bintemp/' not in entry.lower()): # Ignore all generated stuff!
                                includes[entry] = None

                # System includes
                for env in self.all_envs:
                    for entry in self.all_envs[env]['INCLUDES']:
                        if isinstance(entry, Node.Node):
                            entry = entry.abspath()

                        entry = os.path.normpath(entry)
                        includes[entry] = None

                sources = set()
                for entry in to_list(getattr(task_generator, 'source', [])):
                    if isinstance(entry, Node.Node):
                        entry = entry.abspath()

                    if not os.path.isabs(entry):
                      entry = os.path.join(path, entry)

                    entry = os.path.normpath(entry)
                    if ('\\bintemp\\' not in entry.lower()) and ('/bintemp/' not in entry.lower()): # Ignore all generated stuff!
                        sources.add(entry)

                module = OrderedDict([
                  ('name', target_name),
                  ('location', path),
                  ('includes', list(includes.keys())),
                  ('sources', sorted(sources)),
                  ('dependencies', sorted(dependencies))
                ])

                output_file = os.path.join(output_dir, target_name + '.json')
                with open(output_file, 'wt') as ostrm:
                    json.dump(module, ostrm, indent=2, separators=(',', ': '))
                    ostrm.flush()

        CONFIGURED_3RD_PARTY_USELIBS = self.read_and_mark_3rd_party_libs()
        SDKs = self.tp.content.get('SDKs')
        third_party_root = self.tp.content.get('3rdPartyRoot')

        def _find_sdk_location(config):
            names = [config['name']] if isinstance(config['name'], str) else config['name']
            for name in names:
                if name in SDKs:
                    relpath = SDKs[name]['base_source']
                    return os.path.join(third_party_root, relpath)

            source = config['source']
            if '@3P' in source:
                sdk_alias = source[source.find(':') + 1 : -1]
                if sdk_alias in SDKs:
                    relpath = SDKs[sdk_alias]['base_source']
                    return os.path.join(third_party_root, relpath)

            if '@ROOT@' in source:
                relpath = source[source.find('@ROOT@') + 6:]
                return os.path.join(self.engine_path, relpath)

            return None

        for artifact_name in set(third_party_artifacts):
            includes_key = 'INCLUDES_{}'.format(artifact_name)

            includes = OrderedDict()
            for env in self.all_envs:
                for path in self.all_envs[env][includes_key]:
                    includes[path] = None

            lib_config_file, _ = CONFIGURED_3RD_PARTY_USELIBS.get(artifact_name, (None, None))
            config, _, _ = self.get_3rd_party_config_record(lib_config_file) if lib_config_file else (None, None, None)
            location = _find_sdk_location(config) if config else None

            if not location:
                location = third_party_root
                Logs.error('SDK location not found for: ' + artifact_name)

            module = OrderedDict([
              ('name', artifact_name),
              ('location', os.path.normpath(location)),
              ('includes', list(includes.keys()))
            ])

            output_file = os.path.join(output_dir, artifact_name + '.json')
            with open(output_file, 'wt') as ostrm:
                json.dump(module, ostrm, indent=2, separators=(',', ': '))
                ostrm.flush()
