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)
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
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)
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
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)
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'
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
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 ])
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'))
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
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
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), ]
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)
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])
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'))
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
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()
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()
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)
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())
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)
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()
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
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)