Ejemplo n.º 1
0
    def process_generated_output(self):
        # This should only ever be executed on the main thread! Node operations cannot happen during run()
        for generated_output in self.generated_output:
            output_path = generated_output['output_path']
            should_add_to_build = generated_output['should_add_to_build']

            # Nodes won't work with absolute paths, so we have to remove the build path from
            # the given output file path. Given path is unicode, make it str to match Node.
            output_path = os.path.relpath(
                str(output_path), start=self.generator.bld.bldnode.abspath())
            with task_node_access_lock:
                output_node = self.generator.bld.bldnode.find_node(output_path)

            if output_node is None:
                raise Errors.WafError(
                    '[ERROR] az_code_gen: Unable to find generated file as node {}'
                    .format(output_path))

            append_to_unique_list(self.outputs, output_node)

            if should_add_to_build:
                # Add to persistent link list
                self.azcg_append_unique('link_inputs', output_node)
                # Perform actual add to link task
                self.add_link_task(output_node)
Ejemplo n.º 2
0
def get_additional_code_folders_from_spec(ctx):
    spec_list = ctx.get_current_spec_list()
    additional_code_folders = []
    for spec in spec_list:
        spec_additional_folders = ctx.spec_additional_code_folders(spec)
        append_to_unique_list(additional_code_folders, spec_additional_folders)
    return additional_code_folders
Ejemplo n.º 3
0
 def apply_dependencies(args):
     for dep in dependency_objects:
         dep_include = dep.get_include_path()
         if os.path.exists(dep_include.abspath()):
             append_to_unique_list(args['includes'], dep_include)
         if Gem.LinkType.requires_linking(ctx, dep.link_type):
             append_to_unique_list(args['use'], dep.name)
Ejemplo n.º 4
0
def spec_game_projects(ctx, spec_name=None):
    """
    Get and game projects defined for the spec.  Will strip out duplicate entries
    """
    project_settings_map = ctx.get_project_settings_map()

    game_projects = _spec_entry(ctx, 'game_projects', spec_name)
    if game_projects is None:
        return []
    unique_list = list()
    for game_project in game_projects:
        if game_project in project_settings_map:
            append_to_unique_list(unique_list, game_project)

    # Check if this spec has any game->folder map
    global GAME_FOLDER_MAP
    spec_game_folder_map_list = _spec_entry(ctx, 'game_folders', spec_name)
    if spec_game_folder_map_list and len(spec_game_folder_map_list) > 0:
        # If there is an override game folder map in the spec, then validate its uniqueness and add it to the map
        spec_game_folder_map = spec_game_folder_map_list[0]
        for game_name, game_folder in spec_game_folder_map.items():
            if game_name in GAME_FOLDER_MAP:
                current_game_folder = GAME_FOLDER_MAP[game_name]
                if current_game_folder != game_folder:
                    raise Errors.WafError(
                        'Conflicting game name to folder map detected in spec {}'
                        .format(spec_name))
            else:
                GAME_FOLDER_MAP[game_name] = game_folder

    return unique_list
Ejemplo n.º 5
0
    def post_run(self):
        if hasattr(self, 'cached'):
            # Also add the raw output path
            self.generator.env.append_unique('INCPATHS', get_azcg_output_dir_node(self.generator).abspath())

            # Also add paths we stored from prior builds
            azcg_paths = self.azcg_get('AZCG_INCPATHS', [])
            self.propagate_azcg_incpaths(azcg_paths)

            # link_inputs is a list of nodes that need to be added to the link each time
            for link_node in self.azcg_get('link_inputs', []):
                if not self.add_link_task(link_node):
                    return Task.EXCEPTION

            self.generator.source += self.outputs
        else:
            # Register output files generated by the code gen execution
            self.process_generated_output()

            bld = self.generator.bld
            dep_node = None
            resolved_nodes = []

            # Resolve registered dependencies we got into dependency nodes
            for path in self.registered_dependencies:
                dep_node = self.get_node_from_dependency_path(path)

                if dep_node:
                    if not (dep_node.is_child_of(bld.srcnode) or dep_node.is_child_of(bld.bldnode)):
                        # System library
                        continue

                    if dep_node in self.inputs:
                        # Self-dependency
                        continue

                    if dep_node in self.outputs:
                        # Circular dependency
                        continue

                    append_to_unique_list(resolved_nodes, dep_node)
                else:
                    Logs.error('az_code_gen: Unable to find dependency file as node: {}'.format(path))

            # Add azcg_deps and script nodes as dependencies
            for dep_node in itertools.chain(self.azcg_deps, self.script_nodes):
                append_to_unique_list(resolved_nodes, dep_node)

            bld.node_deps[self.uid()] = resolved_nodes

            # force waf to recompute a full signature for this task (we may have new/deleted dependencies we need it to account for)
            try:
                del self.cache_sig
            except:
                pass

            self.azcg_set('AZCG_OUTPUTS', self.outputs)

        Task.Task.post_run(self)
Ejemplo n.º 6
0
 def apply_dependencies(args):
     for dep in dependency_objects:
         dep_include = dep.get_include_path()
         if os.path.exists(dep_include):
             append_to_unique_list(args['includes'], dep_include)
         for module in dep.modules:
             if module.requires_linking(ctx):
                 append_to_unique_list(args['use'], module.target_name)
def load_android_armv7_common_settings(conf):
    """
    Setup all compiler and linker settings shared over all android armv7 configurations
    """

    env = conf.env
    env['ANDROID_ARCH'] = 'armeabi-v7a'

    ndk_root = env['ANDROID_NDK_HOME']
    ndk_rev = env['ANDROID_NDK_REV_MAJOR']

    is_ndk_19_plus = (ndk_rev >= 19)

    defines = [
        'LINUX32',
        '__ARM_NEON__',
    ]
    append_to_unique_list(env['DEFINES'], defines)

    common_flags = [
        '-mfloat-abi=softfp',  # float ABI: hardware code gen, soft calling convention
        '-mfpu=neon',  # enable neon, implies -mfpu=VFPv3-D32
    ]

    link_flags = [
        '-Wl,--fix-cortex-a8',  # required to fix a bug in some Cortex-A8 implementations for neon support
        '-Wl,--icf=safe',  # removes duplicate code
    ]

    if is_ndk_19_plus:
        common_flags += ['-mthumb', '-fpic']

    else:
        platform_root_compile = os.path.join(ndk_root, 'sysroot')
        platform_root_link = os.path.join(ndk_root, 'platforms',
                                          env['ANDROID_NDK_PLATFORM'],
                                          'arch-arm')

        env['INCLUDES'] += [
            os.path.join(platform_root_compile, 'usr', 'include'),
        ]

        common_flags = [
            '--sysroot={}'.format(platform_root_compile),
            '-isystem',
            os.path.join(platform_root_compile, 'usr', 'include',
                         'arm-linux-androideabi'),
        ] + common_flags

        env['LIBPATH'] += [os.path.join(platform_root_link, 'usr', 'lib')]

        link_flags = ['--sysroot={}'.format(platform_root_link)] + link_flags

    env['CFLAGS'] += common_flags[:]
    env['CXXFLAGS'] += common_flags[:]

    env['LINKFLAGS'] += link_flags[:]
def load_android_armv7_common_settings(conf):
    """
    Setup all compiler and linker settings shared over all android armv7 configurations
    """

    env = conf.env
    ndk_root = env['ANDROID_NDK_HOME']

    # common settings for all android armv7 builds
    platform_root_compile = os.path.join(ndk_root, 'platforms',
                                         env['ANDROID_NDK_PLATFORM'],
                                         'arch-arm')
    platform_root_link = platform_root_compile

    if conf.is_using_android_unified_headers():
        platform_root_compile = os.path.join(ndk_root, 'sysroot')

    env['INCLUDES'] += [
        os.path.join(platform_root_compile, 'usr', 'include'),
    ]

    system_root = '--sysroot={}'.format(platform_root_compile)

    common_flags = [
        system_root,
        '-mfloat-abi=softfp',  # float ABI: hardware code gen, soft calling convention
        '-mfpu=neon',  # enable neon, implies -mfpu=VFPv3-D32
    ]

    if conf.is_using_android_unified_headers():
        system_arch = os.path.join(platform_root_compile, 'usr', 'include',
                                   'arm-linux-androideabi')
        common_flags += [
            '-isystem',
            system_arch,
        ]

    env['CFLAGS'] += common_flags[:]
    env['CXXFLAGS'] += common_flags[:]

    defines = [
        'LINUX32',
        '__ARM_NEON__',
    ]
    append_to_unique_list(env['DEFINES'], defines)

    env['LIBPATH'] += [os.path.join(platform_root_link, 'usr', 'lib')]

    system_root = '--sysroot={}'.format(platform_root_link)

    env['LINKFLAGS'] += [
        system_root,
        '-Wl,--fix-cortex-a8',  # required to fix a bug in some Cortex-A8 implementations for neon support
        '-Wl,--icf=safe',  # removes duplicate code
    ]

    env['ANDROID_ARCH'] = 'armeabi-v7a'
Ejemplo n.º 9
0
def get_enabled_game_project_list(self):
    """Utility function which returns the current game projects."""

    # Get either the current spec being built (build) or all the specs for the solution generation (configure/msvs)
    current_spec = getattr(self.options, 'project_spec', '')
    if len(current_spec)==0:
        spec_string_list = getattr(self.options, 'specs_to_include_in_project_generation', '').strip()
        if len(spec_string_list) == 0:
            self.fatal("[ERROR] Missing/Invalid specs ('specs_to_include_in_project_generation') in user_settings.options")
        spec_list = [spec.strip() for spec in spec_string_list.split(',')]
        if len(spec_list) == 0:
            self.fatal("[ERROR] Empty spec list ('specs_to_include_in_project_generation') in user_settings.options")
    else:
        spec_list = [current_spec]

    # Vet the list and make sure all of the specs are valid specs
    for spec in spec_list:
        if not self.is_valid_spec_name(spec):
            self.fatal("[ERROR] Invalid spec '{}'.  Make sure it exists in the specs folder and is a valid spec file.".format(spec))

    # Build up the list of project names
    ordered_unique_list = list()
    for spec in spec_list:
        # Get the projects defined for the particular spec
        projects_for_spec = self.spec_game_projects(spec)

        # If there are no games defined for the spec, and it is not marked with 'disable_game_projects',
        # use the legacy method of getting the enabled game project
        if len(projects_for_spec) == 0 and not self.spec_disable_games(spec):
            projects_for_spec = split_comma_delimited_string(self.options.enabled_game_projects, True)

        # Build up the list
        for project_for_spec in projects_for_spec:
            if len(project_for_spec) > 0:
                append_to_unique_list(ordered_unique_list, project_for_spec)

    # Make sure that the game that is set bootstrap.cfg is specified as an enabled game project, otherwise produce a warning if we
    # have specified at least one game project during build commands
    if len(ordered_unique_list) > 0 and self.cmd.startswith('build_'):
        bootstrap_game = self.get_bootstrap_game()
        bootstrap_game_enabled = False
        for enabled_game in ordered_unique_list:
            if enabled_game == bootstrap_game:
                bootstrap_game_enabled = True
        if not bootstrap_game_enabled:
            if len(ordered_unique_list) > 1:
                self.warn_once("Game project '{}' configured in bootstrap.cfg is not an enabled game for this build.  "
                               "In order to run or debug for the any of the enabled games '{}', one of them needs to be set in bootstrap.cfg under the "
                               "'sys_game_folder' entry accordingly".format(bootstrap_game, ','.join(ordered_unique_list)))
            else:
                self.warn_once("Game project '{}' configured in bootstrap.cfg is the enabled game for this build.  "
                               "In order to run or debug for the game '{}', they need to be set in bootstrap.cfg under the "
                               "'sys_game_folder' entry accordingly".format(bootstrap_game, ordered_unique_list[0]))

    return ordered_unique_list
Ejemplo n.º 10
0
def load_test_settings(ctx):
    """
    Setup all compiler and linker settings shared over all dedicated configurations
    """
    # Setup defines
    append_to_unique_list(
        ctx.env['DEFINES'],
        [
            'AZ_TESTS_ENABLED',
            'AZCORE_ENABLE_MEMORY_TRACKING'  # Enable memory tracking for all unit tests
        ])
Ejemplo n.º 11
0
def load_cryengine_common_settings(conf):
    """
    Setup all platform, compiler and configuration agnostic settings
    """
    v = conf.env

    if conf.is_option_true('enable_memory_tracking'):
        append_to_unique_list(v['DEFINES'], 'AZCORE_ENABLE_MEMORY_TRACKING')
    
    # To allow pragma comment (lib, 'SDKs/...) uniformly, pass Code to the libpath
    append_to_unique_list(v['LIBPATH'], conf.CreateRootRelativePath('Code'))
Ejemplo n.º 12
0
def spec_game_projects(ctx, spec_name=None):
    """
    Get and game projects defined for the spec.  Will strip out duplicate entries
    """

    game_projects = _spec_entry(ctx, 'game_projects', spec_name)
    if game_projects is None:
        return []
    unique_list = list()
    for game_project in game_projects:
        append_to_unique_list(unique_list, game_project)
    return unique_list
Ejemplo n.º 13
0
def spec_restricted_launchers(ctx, spec_name = None):
    """ For a given spec, See if we are restricting particular launchers for the spec"""
    if spec_name is None and len(ctx.options.project_spec) == 0:
        specs_to_restrict = [spec for spec in ctx.loaded_specs() if not ctx.spec_disable_games(spec)]
        restricted_launcher_list = []
        for spec in specs_to_restrict:
            spec_restrict_launchers = _spec_entry(ctx, 'restricted_launchers', spec)
            append_to_unique_list(restricted_launcher_list, spec_restrict_launchers)
        return restricted_launcher_list
    else:
        additional_launcher_projects = _spec_entry(ctx, 'restricted_launchers', spec_name)
    return additional_launcher_projects
Ejemplo n.º 14
0
def spec_additional_launchers(ctx, spec_name = None):
    """ For a given spec, add any additional custom launchers"""
    if spec_name is None and len(ctx.options.project_spec)==0:
        specs_to_include = [spec for spec in ctx.loaded_specs() if not ctx.spec_disable_games(spec)]
        additional_launcher_for_all_specs = []
        for spec in specs_to_include:
            spec_additional_launchers = _spec_entry(ctx, 'additional_launchers', spec)
            append_to_unique_list(additional_launcher_for_all_specs, spec_additional_launchers)
        return additional_launcher_for_all_specs
    else:
        additional_launcher_projects = _spec_entry(ctx, 'additional_launchers', spec_name)
    return additional_launcher_projects
def load_android_armv8_common_settings(conf):
    """
    Setup all compiler and linker settings shared over all android armv8 configurations
    """

    # remove the armv8 android build target if it doesn't meet the min API requirement.
    # letting the platform finish configuring is harmless.
    if (not conf.is_android_armv8_api_valid()) and (
            'android_armv8_clang' in conf.get_supported_platforms()):
        Logs.warn(
            '[WARN] Attempting to configure Android ARMv8 with an API that is lower than the min spec: API 21.  Disabling the Android ARMv8 build target.'
        )
        conf.remove_platform_from_available_platforms('android_armv8_clang')

    env = conf.env
    env['ANDROID_ARCH'] = 'arm64-v8a'

    ndk_root = env['ANDROID_NDK_HOME']
    ndk_rev = env['ANDROID_NDK_REV_MAJOR']

    is_ndk_19_plus = (ndk_rev >= 19)

    defines = [
        'LINUX64',
        '__ARM_NEON__',
    ]
    append_to_unique_list(env['DEFINES'], defines)

    if not is_ndk_19_plus:
        platform_root_compile = os.path.join(ndk_root, 'sysroot')
        platform_root_link = os.path.join(ndk_root, 'platforms',
                                          env['ANDROID_NDK_PLATFORM'],
                                          'arch-arm64')

        env['INCLUDES'] += [
            os.path.join(platform_root_compile, 'usr', 'include'),
        ]

        common_flags = [
            '--sysroot={}'.format(platform_root_compile),
            '-isystem',
            os.path.join(platform_root_compile, 'usr', 'include',
                         'aarch64-linux-android'),
        ]

        env['CFLAGS'] += common_flags[:]
        env['CXXFLAGS'] += common_flags[:]

        env['LIBPATH'] += [os.path.join(platform_root_link, 'usr', 'lib')]

        env['LINKFLAGS'] += [
            '--sysroot={}'.format(platform_root_link),
        ]
Ejemplo n.º 16
0
def apply_required_gems_to_context(ctx, target, kw):

    required_gems = ctx.get_required_gems()

    Logs.debug('gems: adding required gems to target {} : %s'.format(
        target, ','.join([gem.name for gem in required_gems])))

    for gem in required_gems:
        if gem.name != target:
            append_to_unique_list(kw['includes'],
                                  gem.get_include_path().abspath())
            if Gem.LinkType.requires_linking(ctx, gem.link_type):
                append_to_unique_list(kw['use'], gem.name)
Ejemplo n.º 17
0
 def merge_kw_key(self, target, kw_key, source_section, merge_kw):
     
     if not kw_key in source_section:
         return
     
     elif not kw_key in merge_kw:
         merge_kw[kw_key] = source_section[kw_key] # TODO: deep copy?
         
     else:
         if not self.merge_kw_key_type(target, kw_key, str, source_section, merge_kw) and not self.merge_kw_key_type(target, kw_key, bool, source_section, merge_kw):
             # Everything else is meant to be a list of string
             target_list = merge_kw.setdefault(kw_key,[])
             append_to_unique_list(target_list, source_section[kw_key])
Ejemplo n.º 18
0
def load_cryengine_common_settings(conf):
    """
    Setup all platform, compiler and configuration agnostic settings
    """
    v = conf.env

    # Generate CODE_BASE_FOLDER define to allow to create absolute paths in source to use for pragma comment lib 
    code_node = conf.srcnode.make_node('Code')
    code_path = code_node.abspath()
    code_path = code_path.replace('\\', '/')
    v['DEFINES'] += [ 'CODE_BASE_FOLDER="' + code_path + '/"' ]
    
    # To allow pragma comment (lib, 'SDKs/...) uniformly, pass Code to the libpath
    append_to_unique_list(v['LIBPATH'], conf.CreateRootRelativePath('Code'))
Ejemplo n.º 19
0
def get_available_launchers(self, project):

    if self.is_option_true('use_unified_launcher'):
        return {'modules': ['ClientLauncher', 'ServerLauncher']}

    launchers_to_exclude = set()
    if not self.is_target_platform_enabled('android'):
        # If android is disabled, remove add it to the launchers to exclude
        launchers_to_exclude.add('AndroidLauncher')

    # Get the dictionary for the launchers
    available_launchers = {
        'modules': [
            fl for fl in AVAILABLE_LAUNCHERS['modules']
            if fl not in launchers_to_exclude
        ]
    }

    # Get the list of all the launchers
    projects_settings = self.get_project_settings_map()
    additional_launchers = projects_settings.get(project, {}).get(
        'additional_launchers', [])

    for p0, _, _, _ in self.env['RESTRICTED_PLATFORMS']:
        restricted_launcher_name = '{}Launcher'.format(p0)
        append_to_unique_list(additional_launchers, restricted_launcher_name)
        pass

    # Update the modules in the dictionary to include the additional launchers
    for additional_launcher in additional_launchers:
        if additional_launcher not in available_launchers['modules']:
            available_launchers['modules'].append(additional_launcher)

    # Check if there is a list of restricted launchers
    restricted_launchers = projects_settings.get(project, {}).get(
        'restricted_launchers', [])

    # Remove the modules in the dictionary if there is a restricted list
    if len(restricted_launchers) > 0:
        for key in available_launchers:
            modules = available_launchers[key]
            launchers = [
                launcher for launcher in modules
                if launcher in restricted_launchers
            ]
            available_launchers[key] = launchers

    return available_launchers
def initialize_lumberyard(ctx):
    """
    Setup all platform, compiler and configuration agnostic settings
    """
    v = ctx.env

    if conf.is_option_true('enable_memory_tracking'):
        append_to_unique_list(v['DEFINES'], 'AZCORE_ENABLE_MEMORY_TRACKING')

    # BEGIN JAVELIN MOD: https://jira.agscollab.com/browse/JAV-18779 Allows for an AZ Allocator to be used for memory management
    # removed below if check because of issues related to https://jira.agscollab.com/browse/LYAJAV-126
    # if conf.is_option_true('use_az_allocator_for_cry_memory_manager'):
    #    append_to_unique_list(v['DEFINES'], 'USE_AZ_ALLOCATOR_FOR_CRY_MEMORY_MANAGER')
    v['DEFINES'] += ['USE_AZ_ALLOCATOR_FOR_CRY_MEMORY_MANAGER']
    # END JAVELIN MOD

    # To allow pragma comment (lib, 'SDKs/...) uniformly, pass Code to the libpath
    append_to_unique_list(v['LIBPATH'], conf.CreateRootRelativePath('Code'))
    return True
Ejemplo n.º 21
0
def load_android_common_settings(conf):
    """
    Setup all compiler and linker settings shared over all android configurations
    """

    env = conf.env
    ndk_root = env['ANDROID_NDK_HOME']

    # common settings for all android builds
    defines = [
        '_LINUX',
        'LINUX',
        'ANDROID',
        'MOBILE',
        '_HAS_C9X',
        'ENABLE_TYPE_INFO',
        'NDK_REV_MAJOR={}'.format(env['ANDROID_NDK_REV_MAJOR']),
        'NDK_REV_MINOR={}'.format(env['ANDROID_NDK_REV_MINOR']),
    ]

    if conf.is_using_android_unified_headers():
        defines += [
            '__ANDROID_API__={}'.format(env['ANDROID_NDK_PLATFORM_NUMBER']),
        ]

    append_to_unique_list(env['DEFINES'], defines)

    env['LIB'] += [
        'android',  # android library
        'c',  # c library for android
        'log',  # log library for android
        'dl',  # dynamic library
    ]

    env['LINKFLAGS'] += [
        '-rdynamic',  # add ALL symbols to the dynamic symbol table
        '-Wl,--no-undefined'  # tell the gcc linker to fail if it finds undefined references
    ]

    # Pattern to transform outputs
    env['cprogram_PATTERN'] = env['cxxprogram_PATTERN'] = '%s'
    env['cshlib_PATTERN'] = env['cxxshlib_PATTERN'] = 'lib%s.so'
    env['cstlib_PATTERN'] = env['cxxstlib_PATTERN'] = 'lib%s.a'

    env['RPATH_ST'] = '-Wl,-rpath,%s'
    env['SONAME_ST'] = '-Wl,-soname,%s'  # sets the DT_SONAME field in the shared object, used for ELF object loading

    # frameworks aren't supported on Android, disable it
    env['FRAMEWORK'] = []
    env['FRAMEWORK_ST'] = ''
    env['FRAMEWORKPATH'] = []
    env['FRAMEWORKPATH_ST'] = ''

    # java settings
    env['JAVA_VERSION'] = '1.7'
    env['CLASSPATH'] = []

    platform = os.path.join(env['ANDROID_SDK_HOME'], 'platforms',
                            env['ANDROID_SDK_VERSION'])
    android_jar = os.path.join(platform, 'android.jar')

    env['JAVACFLAGS'] = [
        '-encoding',
        'UTF-8',
        '-bootclasspath',
        android_jar,
        '-target',
        env['JAVA_VERSION'],
    ]

    # android interface processing
    env['AIDL_PREPROC_ST'] = '-p%s'
    env['AIDL_PREPROCESSES'] = [os.path.join(platform, 'framework.aidl')]

    # appt settings
    env['APPT_RESOURCE_ST'] = ['-S']
    env['APPT_RESOURCES'] = []

    env['APPT_INLC_ST'] = ['-I']
    env['APPT_INCLUDES'] = [android_jar]
    env['APP_PACKAGE_FLAGS'] = []

    # apk packaging settings
    env['ANDROID_MANIFEST'] = ''
    env['ANDROID_DEBUG_MODE'] = ''

    # jarsigner settings
    env['KEYSTORE_ALIAS'] = conf.get_android_env_keystore_alias()
    env['KEYSTORE'] = conf.get_android_env_keystore_path()
Ejemplo n.º 22
0
def load_android_common_settings(conf):
    """
    Setup all compiler and linker settings shared over all android configurations
    """

    env = conf.env
    ndk_root = env['ANDROID_NDK_HOME']

    # common settings for all android builds
    defines = [
        '_LINUX',
        'LINUX',
        'ANDROID',
        'MOBILE',
        '_HAS_C9X',
        'ENABLE_TYPE_INFO',
        'NDK_REV_MAJOR={}'.format(env['ANDROID_NDK_REV_MAJOR']),
        'NDK_REV_MINOR={}'.format(env['ANDROID_NDK_REV_MINOR']),
    ]

    if conf.is_using_android_unified_headers():
        defines += [
            '__ANDROID_API__={}'.format(env['ANDROID_NDK_PLATFORM_NUMBER']),
        ]

    append_to_unique_list(env['DEFINES'], defines)

    env['CFLAGS'] += [
        '-ffunction-sections', '-fdata-sections'
    ]  # Discard unused sections (flag is common to GCC and Clang)

    env['LIB'] += [
        'android',  # android library
        'c',  # c library for android
        'log',  # log library for android
        'dl',  # dynamic library
    ]

    env['LINKFLAGS'] += [
        '-rdynamic',  # add ALL symbols to the dynamic symbol table
        '-Wl,--no-undefined',  # tell the gcc linker to fail if it finds undefined references
        '-Wl,--gc-sections',  # discards unused sections
    ]

    # Pattern to transform outputs
    env['cprogram_PATTERN'] = env['cxxprogram_PATTERN'] = '%s'
    env['cshlib_PATTERN'] = env['cxxshlib_PATTERN'] = 'lib%s.so'
    env['cstlib_PATTERN'] = env['cxxstlib_PATTERN'] = 'lib%s.a'

    env['RPATH_ST'] = '-Wl,-rpath,%s'
    env['SONAME_ST'] = '-Wl,-soname,%s'  # sets the DT_SONAME field in the shared object, used for ELF object loading

    # frameworks aren't supported on Android, disable it
    env['FRAMEWORK'] = []
    env['FRAMEWORK_ST'] = ''
    env['FRAMEWORKPATH'] = []
    env['FRAMEWORKPATH_ST'] = ''

    # java settings
    env['JAVA_VERSION'] = '1.7'
    env['CLASSPATH'] = []

    platform = os.path.join(env['ANDROID_SDK_HOME'], 'platforms',
                            env['ANDROID_SDK_VERSION'])
    android_jar = os.path.join(platform, 'android.jar')

    env['JAVACFLAGS'] = [
        '-encoding',
        'UTF-8',
        '-bootclasspath',
        android_jar,
        '-target',
        env['JAVA_VERSION'],
    ]

    # android interface processing
    env['AIDL_PREPROC_ST'] = '-p%s'
    env['AIDL_PREPROCESSES'] = [os.path.join(platform, 'framework.aidl')]

    # aapt settings
    env['AAPT_ASSETS_ST'] = ['-A']
    env['AAPT_ASSETS'] = []

    env['AAPT_RESOURCE_ST'] = ['-S']
    env['AAPT_RESOURCES'] = []

    env['AAPT_INLC_ST'] = ['-I']
    env['AAPT_INCLUDES'] = [android_jar]

    env['AAPT_PACKAGE_FLAGS'] = ['--auto-add-overlay']

    # apk packaging settings
    env['ANDROID_MANIFEST'] = ''
    env['ANDROID_DEBUG_MODE'] = ''

    # manifest merger settings
    tools_path = os.path.join(env['ANDROID_SDK_HOME'], 'tools', 'lib')
    tools_contents = os.listdir(tools_path)
    tools_jars = [
        entry for entry in tools_contents if entry.lower().endswith('.jar')
    ]

    # try to detect older versions of the merger
    if 'manifest-merger.jar' in tools_jars or 'manifmerger.jar' in tools_jars:
        env['MANIFEST_MERGER_ENTRY_POINT'] = [
            'com.android.manifmerger.Main', 'merge'
        ]
        env['MANIFEST_MERGER_OLD_VERSION'] = True

        if 'manifest-merger.jar' in tools_jars:
            merger_jar = 'manifest-merger.jar'
        else:
            merger_jar = 'manifmerger.jar'

        env['MANIFEST_MERGER_CLASSPATH'] = os.path.join(tools_path, merger_jar)

    else:

        env['MANIFEST_MERGER_ENTRY_POINT'] = 'com.android.manifmerger.Merger'
        env['MANIFEST_MERGER_OLD_VERSION'] = False

        manifest_merger_lib_names = [
            # entry point for the merger
            'manifest-merger',

            # dependent libs
            'sdk-common',
            'common-'
        ]

        manifest_merger_libs = []
        for jar in tools_jars:
            if any(lib_name for lib_name in manifest_merger_lib_names
                   if jar.lower().startswith(lib_name)):
                manifest_merger_libs.append(jar)

        if len(manifest_merger_libs) < len(manifest_merger_lib_names):
            conf.fatal(
                '[ERROR] Failed to find the required file(s) for the Manifest Merger.  Please use the Android SDK Manager to update to the latest SDK Tools version and run the configure command again.'
            )

        env['MANIFEST_MERGER_CLASSPATH'] = os.pathsep.join([
            os.path.join(tools_path, jar_file)
            for jar_file in manifest_merger_libs
        ])

    # zipalign settings
    env['ZIPALIGN_SIZE'] = '4'  # alignment in bytes, e.g. '4' provides 32-bit alignment (has to be a string)

    # jarsigner settings
    env['KEYSTORE_ALIAS'] = conf.get_android_env_keystore_alias()
    env['KEYSTORE'] = conf.get_android_env_keystore_path()
Ejemplo n.º 23
0
def DefineGem(ctx, *k, **kw):
    """
    Gems behave very similarly to engine modules, but with a few extra options
    """
    manager = GemManager.GetInstance(ctx)

    gem_path = ctx.path.parent.path_from(ctx.srcnode)
    gem = manager.get_gem_by_path(gem_path)
    if not gem:
        ctx.cry_error(
            "DefineGem must be called by a wscript file in a Gem's 'Code' folder."
        )

    # Set default properties
    default_settings = {
        'target': gem.name,
        'output_file_name': gem.dll_file,
        'vs_filter': 'Gems',
        'file_list': ["{}.waf_files".format(gem.name.lower())],
        'platforms': ['all'],
        'configurations': ['all'],
        'defines': [],
        'pch': 'Source/StdAfx.cpp',
        'includes': [],
        'lib': [],
        'libpath': [],
        'features': [],
        'use': [],
        'autod_uselib': []
    }

    for key, value in default_settings.iteritems():
        if key not in kw:
            kw[key] = value

    # Link the auto-registration symbols so that Flow Node registration will work
    append_to_unique_list(kw['use'], ['CryAction_AutoFlowNode', 'AzFramework'])

    # Setup includes
    include_paths = []
    local_includes = ['Include', 'Source']

    # If disable_tests=False or disable_tests isn't specified, enable Google test
    disable_test_settings = ctx.GetPlatformSpecificSettings(
        kw, 'disable_tests', ctx.env['PLATFORM'], ctx.env['CONFIGURATION'])
    disable_tests = kw.get('disable_tests',
                           False) or any(disable_test_settings)
    # Disable tests when doing monolithic build, except when doing project generation (which is always monolithic)
    disable_tests = disable_tests or (
        ctx.env['PLATFORM'] != 'project_generator'
        and ctx.spec_monolithic_build())
    # Disable tests on non-test configurations, except when doing project generation
    disable_tests = disable_tests or (
        ctx.env['PLATFORM'] != 'project_generator'
        and 'test' not in ctx.env['CONFIGURATION'])
    if not disable_tests:
        append_to_unique_list(kw['use'], 'AzTest')
        test_waf_files = "{}_tests.waf_files".format(gem.name.lower())
        if ctx.path.find_node(test_waf_files):
            append_to_unique_list(kw['file_list'], test_waf_files)

    # Add local includes
    for local_path in local_includes:
        node = ctx.path.find_node(local_path)
        if node:
            include_paths.append(node)

    # if the gem includes aren't already in the list, ensure they are prepended in order
    gem_includes = []
    for include_path in include_paths:
        if not include_path in kw['includes']:
            gem_includes.append(include_path)
    kw['includes'] = gem_includes + kw['includes']

    # Add includes for dependencies
    for dep_id in gem.dependencies:
        dep = manager.get_gem_by_spec(dep_id)
        if not dep:
            ctx.cry_error(
                'Gem {}({}) has an unmet dependency with ID {}.'.format(
                    gem.id, gem.name, dep_id))
            continue
        append_to_unique_list(kw['includes'], dep.get_include_path())
        if Gem.LinkType.requires_linking(ctx, dep.link_type):
            append_to_unique_list(kw['use'], dep.name)

    ctx.CryEngineSharedLibrary(ctx, *k, **kw)

    # If Gem is marked for having editor module, add it
    if gem.editor_module:
        # If 'editor' object is added to the wscript, ensure it's settings are kept
        editor_kw = kw.get('editor', {})

        # Overridable settings, not to be combined with Game version
        editor_default_settings = {
            'target':
            gem.editor_module,
            'output_file_name':
            gem.editor_dll_file,
            'vs_filter':
            'Gems',
            'platforms': ['win'],
            'configurations':
            ['debug', 'debug_test', 'profile', 'profile_test'],
            'file_list':
            kw['file_list'] + ["{}_editor.waf_files".format(gem.name.lower())],
            'pch':
            kw['pch'],
            'defines':
            kw['defines'],
            'includes':
            kw['includes'],
            'lib':
            kw['lib'],
            'libpath':
            kw['libpath'],
            'features':
            kw['features'],
            'use':
            kw['use'],
            'autod_uselib':
            kw['autod_uselib'] +
            ['QT5CORE', 'QT5QUICK', 'QT5GUI', 'QT5WIDGETS'],
        }

        for key, value in editor_default_settings.iteritems():
            if key not in editor_kw:
                editor_kw[key] = value

        # Include required settings
        append_to_unique_list(editor_kw['features'], 'qt5')
        append_to_unique_list(editor_kw['use'], 'AzToolsFramework')

        # Set up for testing
        if not disable_tests:
            append_to_unique_list(editor_kw['use'], 'AzTest')
            test_waf_files = "{}_editor_tests.waf_files".format(
                gem.name.lower())
            if ctx.path.find_node(test_waf_files):
                append_to_unique_list(editor_kw['file_list'], test_waf_files)

        ctx.CryEngineModule(ctx, *k, **editor_kw)
Ejemplo n.º 24
0
    def post_run(self):
        if hasattr(self, 'cached'):
            # Also add the raw output path
            self.generator.env.append_unique(
                'INCPATHS',
                get_azcg_output_dir_node(self.generator).abspath())

            # Also add paths we stored from prior builds
            azcg_paths = self.azcg_get('AZCG_INCPATHS', [])
            self.propagate_azcg_incpaths(azcg_paths)

            # link_inputs is a list of nodes that need to be added to the link each time
            for link_node in self.azcg_get('link_inputs', []):
                if not self.add_link_task(link_node):
                    return Task.EXCEPTION

            self.generator.source += self.outputs
        else:
            # Register output files generated by the code gen execution
            self.process_generated_output()

            bld = self.generator.bld
            dep_node = None
            resolved_nodes = []

            # Resolve registered dependencies we got into dependency nodes
            for path in self.registered_dependencies:
                dep_node = self.get_node_from_dependency_path(path)

                if dep_node:
                    if not (dep_node.is_child_of(bld.srcnode)
                            or dep_node.is_child_of(bld.bldnode)):
                        # System library
                        continue

                    if dep_node in self.inputs:
                        # Self-dependency
                        continue

                    if dep_node in self.outputs:
                        # Circular dependency
                        continue

                    append_to_unique_list(resolved_nodes, dep_node)
                else:
                    Logs.error(
                        'az_code_gen: Unable to find dependency file as node: {}'
                        .format(path))

            # Add azcg_deps and script nodes as dependencies
            for dep_node in itertools.chain(self.azcg_deps, self.script_nodes):
                append_to_unique_list(resolved_nodes, dep_node)

            bld.node_deps[self.uid()] = resolved_nodes

            # force waf to recompute a full signature for this task (we may have new/deleted dependencies we need it to account for)
            try:
                del self.cache_sig
            except:
                pass

            self.azcg_set('AZCG_OUTPUTS', self.outputs)

        Task.Task.post_run(self)

        # Due to #includes of code generator header files, we can have an output node which is also an input node.
        # In addition, we are taking nodes that are not originally build nodes (e.g. header files) and building them, which alters the signature flow in Node.get_bld_sig().
        # Task.post_run() default behavior is to set the Node.sig to the task signature which will change our computed task signature because our outputs are our inputs in same cases.
        # To mitigate this, we must restore the original signature for any file that had a non-build signature previously.
        # However, we do not want to alter the signature for files that will be consumed by later tasks.
        # Therefore, we should restore signatures on any node that is not being added to the build (any output nodes not in link_task).
        for output in self.outputs:
            if not output in self.azcg_get('link_inputs', []):
                output.sig = output.cache_sig = Utils.h_file(output.abspath())
Ejemplo n.º 25
0
    def process(self):
        """
        Process current directory for gems

        Note that this has to check each game project to know which gems are enabled
        and build a list of all enabled gems so that those are built.
        To debug gems output during build, use --zones=gems in your command line
        """

        this_path = self.ctx.path

        append_to_unique_list(self.search_paths,
                              os.path.normpath(this_path.abspath()))

        # Parse Gems search path
        config = RawConfigParser()
        if config.read(
                this_path.make_node(
                    'SetupAssistantUserPreferences.ini').abspath()):
            if config.has_section(GEMS_FOLDER) and config.has_option(
                    GEMS_FOLDER, 'SearchPaths\\size'):
                # Parse QSettings style array (i.e. read 'size' attribute, then 1-based-idx\Path)
                array_len = config.getint(GEMS_FOLDER, 'SearchPaths\\size')
                for i in range(0, array_len):
                    new_path = config.get(
                        GEMS_FOLDER, 'SearchPaths\\{}\\Path'.format(i + 1))
                    new_path = os.path.normpath(new_path)
                    Logs.debug('gems: Adding search path {}'.format(new_path))
                    append_to_unique_list(self.search_paths,
                                          os.path.normpath(new_path))

        if not self.ctx.is_engine_local():
            append_to_unique_list(self.search_paths,
                                  os.path.realpath(self.ctx.engine_path))

        # Load all the gems under the Gems folder to search for required gems
        self.required_gems = self.ctx.load_required_gems()

        game_projects = self.ctx.get_enabled_game_project_list()

        for game_project in game_projects:
            Logs.debug('gems: Game Project: %s' % game_project)

            gems_list_file = self.ctx.get_project_node(game_project).make_node(
                GEMS_LIST_FILE)

            if not os.path.isfile(gems_list_file.abspath()):
                if self.ctx.is_option_true('gems_optional'):
                    Logs.debug("gems: Game has no gems file, skipping [%s]" %
                               gems_list_file)
                    continue  # go to the next game
                else:
                    self.ctx.cry_error('Project {} is missing {} file.'.format(
                        game_project, GEMS_LIST_FILE))

            Logs.debug('gems: reading gems file at %s' % gems_list_file)

            gem_info_list = self.ctx.parse_json_file(gems_list_file)
            list_reader = _create_field_reader(
                self.ctx, gem_info_list,
                'Gems list for project ' + game_project)

            # Verify that the project file is an up-to-date format
            gem_format_version = list_reader.field_int('GemListFormatVersion')
            if gem_format_version != GEMS_FORMAT_VERSION:
                self.ctx.cry_error(
                    'Gems list file at {} is of version {}, not expected version {}. Please update your project file.'
                    .format(gems_list_file, gem_format_version,
                            GEMS_FORMAT_VERSION))

            for idx, gem_info_obj in enumerate(list_reader.field_req('Gems')):
                # String for error reporting.
                reader = _create_field_reader(
                    self.ctx, gem_info_obj,
                    'Gem {} in game project {}'.format(idx, game_project))

                gem_id = reader.uuid()
                version = reader.version()
                path = os.path.normpath(reader.field_req('Path'))

                gem = self.get_gem_by_spec(gem_id, version, path)
                if not gem:
                    Logs.debug(
                        'gems: Gem not found in cache, attempting to load from disk: ({}, {}, {})'
                        .format(gem_id, version, path))

                    detected_gem_versions = {}

                    for search_path in self.search_paths:
                        def_file = os.path.join(search_path, path,
                                                GEMS_DEFINITION_FILE)
                        if not os.path.isfile(def_file):
                            continue  # Try again with the next path

                        gem = Gem(self.ctx)
                        gem.path = path
                        gem.abspath = os.path.join(search_path, path)
                        gem.load_from_json(
                            self.ctx.parse_json_file(
                                self.ctx.root.make_node(def_file)))

                        # Protect against loading duplicate gems from different locations, showing a warning if detected
                        dup_gem = detected_gem_versions.get(
                            gem.version.__str__(), None)
                        if dup_gem is not None:
                            Logs.warn(
                                '[WARN] Duplicate gem {} (version {}) found in multiple paths.  Accepting the one at {}'
                                .format(gem.name, gem.version,
                                        dup_gem.abspath))
                            gem = dup_gem
                            break
                        detected_gem_versions[gem.version.__str__()] = gem

                        # Validate that the Gem loaded from the path specified actually matches the id and version.
                        if gem.id != gem_id:
                            self.ctx.cry_error(
                                "Gem at path {} has ID {}, instead of ID {} specified in {}'s {}."
                                .format(path, gem.id, gem_id, game_project,
                                        GEMS_LIST_FILE))

                        if gem.version != version:
                            self.ctx.cry_error(
                                "Gem at path {} has version {}, instead of version {} specified in {}'s {}."
                                .format(path, gem.version, version,
                                        game_project, GEMS_LIST_FILE))

                        self.add_gem(gem)

                if not gem:
                    self.ctx.cry_error(
                        'Failed to load from path "{}"'.format(path))

                gem.games_enabled_in.append(game_project)

        for gem in self.gems:
            Logs.debug("gems: gem %s is used by games: %s" %
                       (gem.name, gem.games_enabled_in))

        # Always add required gems to the gems manager
        for required_gem in self.required_gems:
            self.add_gem(required_gem)
Ejemplo n.º 26
0
def load_android_common_settings(conf):
    """
    Setup all compiler and linker settings shared over all android configurations
    """

    env = conf.env
    ndk_root = env['ANDROID_NDK_HOME']

    defines = []

    if env['ANDROID_NDK_REV_MAJOR'] < 19:
        defines += [
            '__ANDROID_API__={}'.format(env['ANDROID_NDK_PLATFORM_NUMBER']),
        ]

    append_to_unique_list(env['DEFINES'], defines)

    # Pattern to transform outputs
    env['cprogram_PATTERN'] = env['cxxprogram_PATTERN'] = '%s'
    env['cshlib_PATTERN'] = env['cxxshlib_PATTERN'] = 'lib%s.so'
    env['cstlib_PATTERN'] = env['cxxstlib_PATTERN'] = 'lib%s.a'

    env['RPATH_ST'] = '-Wl,-rpath,%s'
    env['SONAME_ST'] = '-Wl,-soname,%s'  # sets the DT_SONAME field in the shared object, used for ELF object loading

    # frameworks aren't supported on Android, disable it
    env['FRAMEWORK'] = []
    env['FRAMEWORK_ST'] = ''
    env['FRAMEWORKPATH'] = []
    env['FRAMEWORKPATH_ST'] = ''

    # java settings
    env['JAVA_VERSION'] = '1.7'
    env['CLASSPATH'] = []

    platform = os.path.join(env['ANDROID_SDK_HOME'], 'platforms',
                            env['ANDROID_SDK_VERSION'])
    android_jar = os.path.join(platform, 'android.jar')

    env['JAVACFLAGS'] = [
        '-encoding',
        'UTF-8',
        '-bootclasspath',
        android_jar,
        '-target',
        env['JAVA_VERSION'],
    ]

    # android interface processing
    env['AIDL_PREPROC_ST'] = '-p%s'
    env['AIDL_PREPROCESSES'] = [os.path.join(platform, 'framework.aidl')]

    # aapt settings
    env['AAPT_ASSETS_ST'] = ['-A']
    env['AAPT_ASSETS'] = []

    env['AAPT_RESOURCE_ST'] = ['-S']
    env['AAPT_RESOURCES'] = []

    env['AAPT_INLC_ST'] = ['-I']
    env['AAPT_INCLUDES'] = [android_jar]

    env['AAPT_PACKAGE_FLAGS'] = ['--auto-add-overlay']

    # apk packaging settings
    env['ANDROID_MANIFEST'] = ''
    env['ANDROID_DEBUG_MODE'] = ''

    # manifest merger settings
    tools_path = os.path.join(env['ANDROID_SDK_HOME'], 'tools', 'lib')
    tools_contents = os.listdir(tools_path)
    tools_jars = [
        entry for entry in tools_contents if entry.lower().endswith('.jar')
    ]

    manifest_merger_lib_names = [
        # entry point for the merger
        'manifest-merger',

        # dependent libs
        'sdk-common',
        'common'
    ]

    manifest_merger_libs = []
    for jar in tools_jars:
        if any(lib_name for lib_name in manifest_merger_lib_names
               if jar.lower().startswith(lib_name)):
            manifest_merger_libs.append(jar)

    if len(manifest_merger_libs) < len(manifest_merger_lib_names):
        conf.fatal(
            '[ERROR] Failed to find the required file(s) for the Manifest Merger.  Please use the Android SDK Manager to update to the latest SDK Tools version and run the configure command again.'
        )

    env['MANIFEST_MERGER_CLASSPATH'] = os.pathsep.join([
        os.path.join(tools_path, jar_file) for jar_file in manifest_merger_libs
    ])

    # zipalign settings
    env['ZIPALIGN_SIZE'] = '4'  # alignment in bytes, e.g. '4' provides 32-bit alignment (has to be a string)

    # jarsigner settings
    env['KEYSTORE_ALIAS'] = conf.get_android_env_keystore_alias()
    env['KEYSTORE'] = conf.get_android_env_keystore_path()
Ejemplo n.º 27
0
def DefineGem(ctx, *k, **kw):
    """
    Gems behave very similarly to engine modules, but with a few extra options
    """
    manager = GemManager.GetInstance(ctx)

    gem = manager.get_gem_by_path(ctx.path.parent.abspath())
    if not gem:
        ctx.cry_error(
            "DefineGem must be called by a wscript file in a Gem's 'Code' folder. Called from: {}"
            .format(ctx.path.abspath()))

    manager.current_gem = gem

    # Generate list of resolved dependencies
    dependency_objects = []
    for dep_id in gem.dependencies:
        dep = manager.get_gem_by_spec(dep_id)
        if not dep:
            unmet_name = find_gem_name_by_id(ctx, dep_id)
            if unmet_name is None:
                ctx.cry_error(
                    'Gem {}({}) has an unmet dependency with ID {} (Unable to locate in disk).'
                    .format(gem.id, gem.name, dep_id))
            else:
                ctx.cry_error(
                    'Gem {}({}) has an unmet dependency with ID {}({}). Please use the Project Configurator to correct this.'
                    .format(gem.id, gem.name, dep_id, unmet_name))

            continue
        dependency_objects.append(dep)

    # Applies dependencies to args list
    def apply_dependencies(args):
        for dep in dependency_objects:
            dep_include = dep.get_include_path()
            if os.path.exists(dep_include):
                append_to_unique_list(args['includes'], dep_include)
            for module in dep.modules:
                if module.requires_linking(ctx):
                    append_to_unique_list(args['use'], module.target_name)

    # Iterate over each module and setup build
    for module in gem.modules:
        if module.name:
            module_kw = kw.get(module.name, None)

            # If no based on name, try lowercasing
            if module_kw == None:
                module_kw = kw.get(module.name.lower(), None)

            # If still no kw, error
            if module_kw == None:
                ctx.cry_error(
                    "Gem {0}'s wscript missing definition for module {1} (valid dict names are {1} and {2}."
                    .format(gem.name, module.name, module.name.lower()))
        else:
            module_kw = kw

        module_file_list_base = module.target_name.replace('.', '_').lower()

        # Set default properties
        default_settings = {
            'target': module.target_name,
            'output_file_name': module.file_name,
            'vs_filter': gem.name if gem.is_game_gem else 'Gems',
            'file_list': ["{}.waf_files".format(module_file_list_base)],
            'platforms': ['all'],
            'configurations': ['all'],
            'defines': [],
            'includes': [],
            'export_includes': [],
            'lib': [],
            'libpath': [],
            'features': [],
            'use': [],
            'uselib': []
        }

        # Builders have some special settings
        if module.type in [
                Gem.Module.Type.Builder, Gem.Module.Type.EditorModule
        ]:
            default_settings['platforms'] = ['win', 'darwin']
            default_settings['configurations'] = [
                'debug', 'debug_test', 'profile', 'profile_test'
            ]

        if module.parent:
            parent_module = None
            for parent_module_itr in gem.modules:
                if (parent_module_itr.name == module.parent or
                    (module.parent == 'GameModule'
                     and parent_module_itr.type == Gem.Module.Type.GameModule
                     and parent_module_itr.name == None)):
                    parent_module = parent_module_itr
                    break
            if not parent_module:
                ctx.cry_error(
                    '{}\'s Gem.json Module "{}" "Extends" non-existent module {}.'
                    .format(gem.name, module.name, module.parent))

            parent_kw = getattr(parent_module, 'kw', None)
            if not parent_kw:
                ctx.cry_error(
                    '{}\'s wscript defines module {} before parent {}, please reverse the order.'
                    .format(gem.name, module.name, module.parent))

            EXTENDABLE_FIELDS = [
                'file_list', 'defines', 'includes', 'features', 'lib',
                'libpath', 'use', 'uselib'
            ]

            INHERITABLE_FIELDS = [
                'pch',
            ]

            for field in EXTENDABLE_FIELDS:
                default_settings[field].extend(parent_kw.get(field, []))

            for field in INHERITABLE_FIELDS:
                parent_value = parent_kw.get(field, None)
                if parent_value:
                    default_settings[field] = parent_value

        # Apply defaults to the project
        for key, value in default_settings.iteritems():
            if key not in module_kw:
                module_kw[key] = value

    # Make it so gems can be replaced while executable is still running
        append_to_unique_list(module_kw['features'], ['link_running_program'])

        # Add tools stuff to the editor modules
        if module.type == Gem.Module.Type.EditorModule:
            append_unique_kw_entry(module_kw, 'features', ['qt5'])
            append_unique_kw_entry(module_kw, 'use',
                                   ['AzToolsFramework', 'AzQtComponents'])
            append_unique_kw_entry(
                module_kw, 'uselib',
                ['QT5CORE', 'QT5QUICK', 'QT5GUI', 'QT5WIDGETS'])

        # If the Gem is a game gem, we may need to apply enabled gems for all of the enabled game projects so it will build
        if gem.is_game_gem and module.type != Gem.Module.Type.Builder:

            # We need to let cryengine_modules.RunTaskGenerator know that this is a game gem and must be built always
            setattr(ctx, 'is_game_gem', True)

            # The gem only builds once, so we need apply the union of all non-game-gem gems enabled for all enabled game projects
            unique_gems = []
            enabled_projects = ctx.get_enabled_game_project_list()
            for enabled_project in enabled_projects:
                gems_for_project = ctx.get_game_gems(enabled_project)
                for gem_for_project in gems_for_project:
                    if gem_for_project.name != gem.name and not gem_for_project.is_game_gem:
                        append_to_unique_list(unique_gems, gem_for_project)

            is_android = ctx.is_android_platform(ctx.env['PLATFORM'])

            for unique_gem in unique_gems:
                if unique_gem.id in gem.dependencies or unique_gem.is_required or is_android:
                    apply_gem_to_kw(ctx, kw, unique_gem)

        working_path = ctx.path.abspath()
        dir_contents = os.listdir(working_path)

        # Setup PCH if disable_pch is false (default), and pch is not set (default)
        if not module_kw.get('disable_pch', False) and module_kw.get(
                'pch', None) == None:

            # default casing for the relative path to the pch file
            source_dir = 'Source'
            pch_file = 'StdAfx.cpp'

            for entry in dir_contents:
                if entry.lower() == 'source':
                    source_dir = entry
                    break

            source_contents = os.listdir(os.path.join(working_path,
                                                      source_dir))
            # see if they have a legacy stdafx precompiled header
            for entry in source_contents:
                if entry.lower() == 'stdafx.cpp':
                    pch_file = entry
                    break
            # if they have a precompiled file then we will prefer that
            for entry in source_contents:
                if entry.lower().endswith('precompiled.cpp'):
                    pch_file = entry
                    break

            # has to be forward slash because the pch is string compared with files
            # in the waf_files which use forward slashes
            module_kw['pch'] = "{}/{}".format(source_dir, pch_file)

        # Apply any additional 3rd party uselibs
        if len(gem.local_uselibs) > 0:
            append_unique_kw_entry(module_kw, 'uselib', gem.local_uselibs)

        # Link the auto-registration symbols so that Flow Node registration will work
        if module.type in [
                Gem.Module.Type.GameModule, Gem.Module.Type.EditorModule
        ]:
            append_unique_kw_entry(module_kw, 'use',
                                   ['CryAction_AutoFlowNode', 'AzFramework'])

        append_unique_kw_entry(module_kw, 'features', ['link_running_program'])

        # If disable_tests=False or disable_tests isn't specified, enable Google test
        disable_test_settings = ctx.GetPlatformSpecificSettings(
            module_kw, 'disable_tests', ctx.env['PLATFORM'],
            ctx.env['CONFIGURATION'])
        disable_tests = module_kw.get('disable_tests',
                                      False) or any(disable_test_settings)
        # Disable tests when doing monolithic build, except when doing project generation (which is always monolithic)
        disable_tests = disable_tests or (
            ctx.env['PLATFORM'] != 'project_generator'
            and ctx.spec_monolithic_build())
        # Disable tests on non-test configurations, except when doing project generation
        disable_tests = disable_tests or (
            ctx.env['PLATFORM'] != 'project_generator'
            and 'test' not in ctx.env['CONFIGURATION'])
        if not disable_tests:
            append_unique_kw_entry(module_kw, 'use', 'AzTest')
            test_waf_files = "{}_tests.waf_files".format(module_file_list_base)
            if ctx.path.find_node(test_waf_files):
                append_unique_kw_entry(module_kw, 'file_list', test_waf_files)

        # Setup includes
        include_paths = []

        # Most gems use the upper case directories, however some have lower case source directories.
        # This will add the correct casing of those include directories
        local_includes = [
            entry for entry in dir_contents
            if entry.lower() in ['include', 'source']
        ]

        # Add local includes
        for local_path in local_includes:
            node = ctx.path.find_node(local_path)
            if node:
                include_paths.append(node)

        # if the gem includes aren't already in the list, ensure they are prepended in order
        gem_includes = []
        for include_path in include_paths:
            if not include_path in module_kw['includes']:
                gem_includes.append(include_path)
        module_kw['includes'] = gem_includes + module_kw['includes']

        # Take the gems include folder if it exists and add it to the export_includes in case the gem is being 'used'
        export_include_node = ctx.path.find_node('Include')
        if export_include_node:
            module_kw['export_includes'] = [export_include_node.abspath()
                                            ] + module_kw['export_includes']

        apply_dependencies(module_kw)

        # Save the build settings so we can access them later
        module.kw = module_kw

        append_unique_kw_entry(module_kw, 'is_gem', True)

        if gem.is_game_gem and ctx.is_monolithic_build(
        ) and ctx.is_android_platform(
                ctx.env['PLATFORM']
        ) and module.type == Gem.Module.Type.GameModule:

            # Special case for android & monolithic builds.  If this is the game gem for the project, it needs to apply the
            # enabled gems here instead of the launcher where its normally applied.  (see cryengine_modules.CryLauncher_Impl
            # for details).  In legacy game projects, this the game dll is declared as a CryEngineModule
            setattr(ctx, 'game_project', gem.name)
            ctx.CryEngineModule(**module_kw)
        else:
            if module.type in [
                    Gem.Module.Type.GameModule, Gem.Module.Type.EditorModule
            ]:
                ctx.CryEngineSharedLibrary(**module_kw)
            elif module.type == Gem.Module.Type.StaticLib:
                ctx.CryEngineStaticLibrary(**module_kw)
            elif module.type == Gem.Module.Type.Builder:
                ctx.BuilderPlugin(**module_kw)

        # INTERNAL USE ONLY
        # Apply export_defines to ENTIRE BUILD. USE LIGHTLY.
        if module.type == Gem.Module.Type.GameModule:
            export_defines = module_kw.get(
                'export_defines', []) + ctx.GetPlatformSpecificSettings(
                    module_kw, 'export_defines', ctx.env['PLATFORM'],
                    ctx.env['CONFIGURATION'])
            append_to_unique_list(ctx.env['DEFINES'], export_defines)

    manager.current_gem = None
Ejemplo n.º 28
0
def DefineGem(ctx, *k, **kw):
    """
    Gems behave very similarly to engine modules, but with a few extra options
    """
    manager = GemManager.GetInstance(ctx)

    gem = manager.get_gem_by_path(ctx.path.parent.abspath())
    if not gem:
        ctx.cry_error(
            "DefineGem must be called by a wscript file in a Gem's 'Code' folder. Called from: {}"
            .format(ctx.path.abspath()))

    # Detect any 3rd party libs in this GEM
    detected_uselib_names = []
    if ctx.cmd.startswith('build_'):

        platform = ctx.env['PLATFORM']
        configuration = ctx.env['CONFIGURATION']
        reported_errors = set()

        path_alias_map = {
            'ROOT': ctx.root.make_node(Context.launch_dir).abspath(),
            'GEM': ctx.path.parent.abspath()
        }
        config_3rdparty_folder = ctx.path.parent.make_node('3rdParty')
        thirdparty_error_msgs, detected_uselib_names = ctx.detect_all_3rd_party_libs(
            config_3rdparty_folder, platform, configuration, path_alias_map,
            False)
        for thirdparty_error_msg in thirdparty_error_msgs:
            if thirdparty_error_msg not in reported_errors:
                reported_errors.add(thirdparty_error_msg)
                Logs.warn('[WARN] {}'.format(thirdparty_error_msg))

    # Set default properties
    default_settings = {
        'target': gem.name,
        'output_file_name': gem.dll_file,
        'vs_filter': gem.name if gem.is_game_gem else 'Gems',
        'file_list': ["{}.waf_files".format(gem.name.lower())],
        'platforms': ['all'],
        'configurations': ['all'],
        'defines': [],
        'includes': [],
        'export_includes': [],
        'lib': [],
        'libpath': [],
        'features': [],
        'use': [],
        'uselib': []
    }

    for key, value in default_settings.iteritems():
        if key not in kw:
            kw[key] = value

    # If the Gem is a game gem, we may need to apply enabled gems for all of the enabled game projects so it will build
    if gem.is_game_gem:
        # The gem only builds once, so we need apply the union of all non-game-gem gems enabled for all enabled game projects
        unique_gems = set()
        enabled_projects = ctx.get_enabled_game_project_list()
        for enabled_project in enabled_projects:
            gems_for_project = ctx.get_game_gems(enabled_project)
            for gem_for_project in gems_for_project:
                if gem_for_project.name != gem.name and not gem_for_project.is_game_gem:
                    unique_gems.add(gem_for_project)

        is_android = ctx.env['PLATFORM'] in ('android_armv7_gcc',
                                             'android_armv7_clang')

        for unique_gem in unique_gems:
            if unique_gem.id in gem.dependencies or unique_gem.is_required or is_android:
                if os.path.exists(unique_gem.get_include_path().abspath()):
                    kw['includes'] += [unique_gem.get_include_path()]
                if Gem.LinkType.requires_linking(ctx, unique_gem.link_type):
                    kw['use'] += [unique_gem.name]

    working_path = ctx.path.abspath()
    dir_contents = os.listdir(working_path)

    # Setup PCH if disable_pch is false (default), and pch is not set (default)
    if not kw.get('disable_pch', False) and kw.get('pch', None) == None:

        # default casing for the relative path to the pch file
        source_dir = 'Source'
        pch_file = 'StdAfx.cpp'

        for entry in dir_contents:
            if entry.lower() == 'source':
                source_dir = entry
                break

        source_contents = os.listdir(os.path.join(working_path, source_dir))
        for entry in source_contents:
            if entry.lower() == 'stdafx.cpp':
                pch_file = entry
                break

        # has to be forward slash because the pch is string compared with files
        # in the waf_files which use forward slashes
        kw['pch'] = "{}/{}".format(source_dir, pch_file)

    # Apply any additional 3rd party uselibs
    if len(detected_uselib_names) > 0:
        append_to_unique_list(kw['uselib'], list(detected_uselib_names))

    # Link the auto-registration symbols so that Flow Node registration will work
    append_to_unique_list(kw['use'], ['CryAction_AutoFlowNode', 'AzFramework'])

    # Make it so gems can be replaced while executable is still running
    append_to_unique_list(kw['features'], ['link_running_program'])

    # Setup includes
    include_paths = []

    # Most gems use the upper case directories, however some have lower case source directories.
    # This will add the correct casing of those include directories
    local_includes = [
        entry for entry in dir_contents
        if entry.lower() in ['include', 'source']
    ]

    # If disable_tests=False or disable_tests isn't specified, enable Google test
    disable_test_settings = ctx.GetPlatformSpecificSettings(
        kw, 'disable_tests', ctx.env['PLATFORM'], ctx.env['CONFIGURATION'])
    disable_tests = kw.get('disable_tests',
                           False) or any(disable_test_settings)
    # Disable tests when doing monolithic build, except when doing project generation (which is always monolithic)
    disable_tests = disable_tests or (
        ctx.env['PLATFORM'] != 'project_generator'
        and ctx.spec_monolithic_build())
    # Disable tests on non-test configurations, except when doing project generation
    disable_tests = disable_tests or (
        ctx.env['PLATFORM'] != 'project_generator'
        and 'test' not in ctx.env['CONFIGURATION'])
    if not disable_tests:
        append_to_unique_list(kw['use'], 'AzTest')
        test_waf_files = "{}_tests.waf_files".format(gem.name.lower())
        if ctx.path.find_node(test_waf_files):
            append_to_unique_list(kw['file_list'], test_waf_files)

    # Add local includes
    for local_path in local_includes:
        node = ctx.path.find_node(local_path)
        if node:
            include_paths.append(node)

    # if the gem includes aren't already in the list, ensure they are prepended in order
    gem_includes = []
    for include_path in include_paths:
        if not include_path in kw['includes']:
            gem_includes.append(include_path)
    kw['includes'] = gem_includes + kw['includes']

    # Take the gems include folder if it exists and add it to the export_includes in case the gem is being 'used'
    export_include_node = ctx.path.find_node('Include')
    if export_include_node:
        kw['export_includes'] = [export_include_node.abspath()
                                 ] + kw['export_includes']

    # Generate list of resolved dependencies
    dependency_objects = []
    for dep_id in gem.dependencies:
        dep = manager.get_gem_by_spec(dep_id)
        if not dep:
            unmet_name = find_gem_name_by_id(ctx, dep_id)
            if unmet_name is None:
                ctx.cry_error(
                    'Gem {}({}) has an unmet dependency with ID {} (Unable to locate in disk).'
                    .format(gem.id, gem.name, dep_id))
            else:
                ctx.cry_error(
                    'Gem {}({}) has an unmet dependency with ID {}({}). Please use the Project Configurator to correct this.'
                    .format(gem.id, gem.name, dep_id, unmet_name))

            continue
        dependency_objects.append(dep)
    # Applies dependencies to args list
    def apply_dependencies(args):
        for dep in dependency_objects:
            dep_include = dep.get_include_path()
            if os.path.exists(dep_include.abspath()):
                append_to_unique_list(args['includes'], dep_include)
            if Gem.LinkType.requires_linking(ctx, dep.link_type):
                append_to_unique_list(args['use'], dep.name)

    apply_dependencies(kw)

    if gem.is_game_gem and ctx.is_monolithic_build(
    ) and ctx.env['PLATFORM'] in ('android_armv7_gcc', 'android_armv7_clang'):

        # Special case for android & monolithic builds.  If this is the game gem for the project, it needs to apply the
        # enabled gems here instead of the launcher where its normally applied.  (see cryengine_modules.CryLauncher_Impl
        # for details).  In legacy game projects, this the game dll is declared as a CryEngineModule
        setattr(ctx, 'game_project', gem.name)
        ctx.CryEngineModule(ctx, *k, **kw)
    else:
        ctx.CryEngineSharedLibrary(ctx, *k, **kw)

    # If Gem is marked for having editor module, add it
    if gem.editor_module and ctx.editor_gems_enabled():
        # If 'editor' object is added to the wscript, ensure it's settings are kept
        editor_kw = kw.get('editor', {})

        # Overridable settings, not to be combined with Game version
        editor_default_settings = {
            'target':
            gem.editor_module,
            'output_file_name':
            gem.editor_dll_file,
            'vs_filter':
            'Gems',
            'platforms': ['win', 'darwin'],
            'configurations':
            ['debug', 'debug_test', 'profile', 'profile_test'],
            'file_list':
            kw['file_list'] + ["{}_editor.waf_files".format(gem.name.lower())],
            'pch':
            kw['pch'],
            'defines':
            list(kw['defines']),
            'includes':
            list(kw['includes']),
            'export_includes':
            list(kw['export_includes']),
            'lib':
            list(kw['lib']),
            'libpath':
            list(kw['libpath']),
            'features':
            list(kw['features']),
            'use':
            list(kw['use']),
            'uselib':
            list(kw['uselib']),
        }

        for key, value in editor_default_settings.iteritems():
            if key not in editor_kw:
                editor_kw[key] = value

        # Include required settings
        append_to_unique_list(editor_kw['features'],
                              ['qt5', 'link_running_program'])
        append_to_unique_list(editor_kw['use'],
                              ['AzToolsFramework', 'AzQtComponents'])
        append_to_unique_list(editor_kw['uselib'],
                              ['QT5CORE', 'QT5QUICK', 'QT5GUI', 'QT5WIDGETS'])

        # Set up for testing
        if not disable_tests:
            append_to_unique_list(editor_kw['use'], 'AzTest')
            test_waf_files = "{}_editor_tests.waf_files".format(
                gem.name.lower())
            if ctx.path.find_node(test_waf_files):
                append_to_unique_list(editor_kw['file_list'], test_waf_files)

        apply_dependencies(editor_kw)

        ctx.CryEngineModule(ctx, *k, **editor_kw)