Last active
July 18, 2016 08:17
-
-
Save uucidl/3bf3795242aefe6846a4 to your computer and use it in GitHub Desktop.
(WIP) FASTbuild generator for gyp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 2c2a5e747a601ec655e5a94d935cca09599580d1 Mon Sep 17 00:00:00 2001 | |
From: nil <[email protected]> | |
Date: Mon, 9 May 2016 12:51:23 +0200 | |
Subject: [PATCH 01/19] Import fastbuild generator | |
--- | |
pylib/gyp/generator/fastbuild.py | 1095 ++++++++++++++++++++++++++++++++++++++ | |
1 file changed, 1095 insertions(+) | |
create mode 100644 pylib/gyp/generator/fastbuild.py | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
new file mode 100644 | |
index 0000000..8cdf52c | |
--- /dev/null | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -0,0 +1,1095 @@ | |
+## | |
+# NOTE(nicolas): | |
+# | |
+# This module generates `.bff` files for consumption with the FASTbuild | |
+# URL(http://fastbuild.org) opensource build system developed by Franta Fulin. | |
+# | |
+# TODO(nicolas): $(IntDir) MSVS macro to be supported. (Or not?) | |
+ | |
+import copy | |
+import gyp | |
+import gyp.common | |
+import gyp.msvs_emulation | |
+import gyp.xcode_emulation | |
+import os | |
+ | |
+from collections import defaultdict | |
+from itertools import product | |
+from pprint import pformat | |
+ | |
+# Utilities | |
+ | |
+ | |
+def enum(*sequential, **named): | |
+ enums = dict(zip(sequential, range(len(sequential))), **named) | |
+ return type('Enum', (), enums) | |
+ | |
+ | |
+def debug_assert(x): | |
+ if fastbuild_debug: | |
+ if not x: | |
+ import pdb | |
+ pdb.set_trace() | |
+ assert(x) | |
+ | |
+# TODO(nil): shouldn't we also search in the runtime path of the toolset? | |
+# for instance we have ml64.exe that needs to be found but that cannot be guaranteed | |
+# when launching the `gyp` command | |
+def shell_which(filename, additional_paths=None): | |
+ if additional_paths is None: | |
+ additional_paths = [] | |
+ | |
+ if os.access(filename, os.F_OK | os.X_OK): | |
+ return filename | |
+ | |
+ path_dirs = [y.strip('"') for y in os.environ["PATH"].split(os.pathsep)] + additional_paths | |
+ extensions = [""] + os.environ.get("PATHEXT", "").split(os.pathsep) | |
+ | |
+ for path_dir, ext in product(path_dirs, extensions): | |
+ filepath = os.path.join(path_dir, filename + ext) | |
+ if os.access(filepath, os.F_OK | os.X_OK): | |
+ return filepath | |
+ debug_assert(False) | |
+ raise ValueError('Could not find executable %s' % filename) | |
+ | |
+# Configuration & Constants | |
+ | |
+fastbuild_debug = True | |
+fastbuild_verbose = False | |
+ | |
+CompilerType = enum( | |
+ C='C', | |
+ CXX='CXX', | |
+ ObjC='ObjC', | |
+ ObjCXX='ObjCXX', | |
+ NONE='NONE' | |
+) | |
+ | |
+compiler_type_extension_index = { | |
+ '.c': CompilerType.C, | |
+ '.cc': CompilerType.CXX, | |
+ '.cpp': CompilerType.CXX, | |
+ '.cxx': CompilerType.CXX, | |
+ '.m': CompilerType.ObjC, | |
+ '.mm': CompilerType.ObjCXX, | |
+ '.h': CompilerType.NONE, | |
+ '.hpp': CompilerType.NONE, | |
+} | |
+ | |
+# GENERATOR API Implementation: | |
+ | |
+# Here are some entry points left undefined: | |
+# | |
+# CalculateGeneratorInputInfo(params) | |
+# generator_additional_non_configuration_keys | |
+# generator_additional_path_sections | |
+# generator_extra_sources_for_rules | |
+# generator_supports_multiple_toolsets | |
+# generator_wants_static_library_dependencies_adjusted | |
+# generator_filelist_paths | |
+ | |
+ | |
+def CalculateVariables(default_variables, params): | |
+ # fastbuild supports windows, mac and linux | |
+ flavor = gyp.common.GetFlavor(params) | |
+ default_variables.setdefault('OS', flavor) | |
+ | |
+# TODO(nicolas): clean this list up | |
+generator_default_variables = { | |
+ 'CONFIGURATION_NAME': '$ConfigName$', | |
+ 'EXECUTABLE_PREFIX': '', | |
+ 'EXECUTABLE_SUFFIX': '', | |
+ 'INTERMEDIATE_DIR': '$IntermediateDir$', | |
+ 'PRODUCT_DIR': '$ProductDir$', | |
+ 'SHARED_INTERMEDIATE_DIR': '$SharedIntermediateDir$', | |
+ 'SHARED_LIB_PREFIX': 'lib', | |
+ 'STATIC_LIB_PREFIX': 'lib', | |
+ 'STATIC_LIB_SUFFIX': '.a', | |
+ # TODO(nicolas): review this when implementing rules | |
+ 'RULE_INPUT_PATH': '%1', | |
+ # This gets expanded by Python. | |
+ 'RULE_INPUT_DIRNAME': '$RuleInputDirname$', | |
+ 'RULE_INPUT_ROOT': '$RuleInputRoot$', | |
+ 'RULE_INPUT_NAME': '$RuleInputName$', | |
+ 'RULE_INPUT_EXT': '$RuleInputExt$', | |
+} | |
+generator_wants_sorted_dependencies = True | |
+ | |
+# main entry point | |
+ | |
+ | |
+def GenerateOutput(target_list, target_dicts, data, params): | |
+ def get_bff_path(build_file, params): | |
+ (build_file_root, _ignored_ext) = os.path.splitext(build_file) | |
+ options = params['options'] | |
+ bff_basename = build_file_root + options.suffix + '.bff' | |
+ if options.generator_output: | |
+ return os.path.join(options.generator_output, bff_basename) | |
+ else: | |
+ return bff_basename | |
+ | |
+ def get_config_names(target_list, target_dicts): | |
+ first_target = target_list[0] | |
+ return target_dicts[first_target]['configurations'].keys() | |
+ | |
+ flavor = gyp.common.GetFlavor(params) | |
+ for build_file in params['build_files']: | |
+ bff_path = get_bff_path(build_file, params) | |
+ | |
+ # TODO(nicolas): profiling | |
+ print 'Generating FASTbuild file:' | |
+ bff = MakeBFFCreator() | |
+ PushHeadingComment(bff, """FASTBuild build file, generated by gyp | |
+For flavor: %s | |
+ """ % flavor) | |
+ PushHardcodedPrologue(bff) | |
+ toolsets_with_bff_config = set() | |
+ config_names = get_config_names(target_list, target_dicts) | |
+ # TODO(nicolas): remove, this is needed for now because I don't know how to | |
+ # avoid duplicating actions which lead to the same output across | |
+ # configs | |
+ config_names = [y for y in config_names if y == 'Debug'] | |
+ | |
+ generator_flags = params.get('generator_flags', {}) | |
+ toolset = MakeToolset(flavor, generator_flags) | |
+ | |
+ for qualified_target in target_list: | |
+ target_build_file, target_name, target_toolset = \ | |
+ gyp.common.ParseQualifiedTarget(qualified_target) | |
+ | |
+ if target_toolset not in toolsets_with_bff_config: | |
+ PushToolsetConfig(bff, target_toolset, config_names) | |
+ toolsets_with_bff_config.add(target_toolset) | |
+ | |
+ target_spec = target_dicts[qualified_target] | |
+ PushHeadingComment(bff, 'target: %s (type: %s)' % | |
+ (target_name, target_spec['type'])) | |
+ if fastbuild_verbose: | |
+ PushComment(bff, "here is the target spec:\n%s" % | |
+ pformat(target_spec)) | |
+ if flavor == 'mac': | |
+ gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec( | |
+ data[build_file], | |
+ target_spec | |
+ ) | |
+ inputs_base_dir = os.path.dirname(target_build_file) | |
+ platform = MakePlatform(flavor, generator_flags, toolset, target_spec) | |
+ PushTargetSpecs( | |
+ bff, | |
+ platform, | |
+ inputs_base_dir, | |
+ qualified_target, | |
+ target_spec, | |
+ target_dicts, | |
+ config_names, | |
+ ) | |
+ BFFFinalize(bff) | |
+ BFFWriteToFile(bff, bff_path) | |
+ | |
+ | |
+def GetBFFConfigVariableName(toolset, config_name): | |
+ return '.%s%sConfig' % (config_name.capitalize(), toolset.capitalize()) | |
+ | |
+ | |
+def PushToolsetConfig(bff, toolset, config_names): | |
+ products_base_dir = os.path.join('output', toolset) | |
+ toolset_config_definition = BFFStruct( | |
+ [ | |
+ BFFAssignment('.Toolset', '%s' % toolset), | |
+ BFFAssignment('.SharedProductDir', '%s' % products_base_dir), | |
+ BFFAssignment( | |
+ '.SharedIntermediateDir', | |
+ '$SharedProductDir$/gen' | |
+ ), | |
+ # Are these ProductDir/IntermediateDir legal when outside of any | |
+ # configuration? TODO(nicolas): or change gyp, or change rules to | |
+ # be generated per config | |
+ BFFAssignment('.ProductDir', '$SharedProductDir$'), | |
+ BFFAssignment( | |
+ '.IntermediateDir', | |
+ '$SharedIntermediateDir$' | |
+ ), | |
+ BFFUsing('.%sLinker' % toolset.capitalize()), | |
+ BFFUsing('.%sLibrarian' % toolset.capitalize()), | |
+ ] | |
+ ) | |
+ PushAssignment(bff, GetBFFConfigVariableName( | |
+ toolset, ''), toolset_config_definition) | |
+ | |
+ for config_name in config_names: | |
+ PushConfig( | |
+ bff, | |
+ GetBFFConfigVariableName(toolset, config_name), | |
+ config_name, | |
+ toolset | |
+ ) | |
+ | |
+ | |
+def PushConfig(bff, bff_config_name, config_name, toolset): | |
+ PushHeadingComment(bff, "%s Configuration" % config_name) | |
+ config_definition = BFFStruct( | |
+ [ | |
+ BFFUsing(GetBFFConfigVariableName(toolset, '')), | |
+ BFFAssignment('.ConfigName', '%s' % config_name), | |
+ BFFAssignment('.ProductDir', '$SharedProductDir$/%s' % | |
+ config_name), | |
+ BFFAssignment('.IntermediateDir', '$ProductDir$/gen'), | |
+ ] | |
+ ) | |
+ PushAssignment(bff, | |
+ bff_config_name, | |
+ config_definition | |
+ ) | |
+ | |
+ | |
+def MakeBFFCreator(): | |
+ return BFFCreator() | |
+ | |
+ | |
+CommandType = enum( | |
+ 'Assignment', | |
+ 'Concatenation', | |
+ 'FunctionBegin', | |
+ 'FunctionEnd', | |
+ 'Raw', | |
+ 'IndentedRaw', | |
+ 'Struct', | |
+ 'Using', | |
+) | |
+ | |
+ | |
+def BFFRaw(s): | |
+ return CommandType.Raw, s | |
+ | |
+ | |
+def BFFIndentedRaw(s): | |
+ return CommandType.IndentedRaw, s | |
+ | |
+ | |
+def BFFUsing(variable_name): | |
+ return CommandType.Using, locals() | |
+ | |
+ | |
+def BFFFunctionBegin(function_name, function_arguments): | |
+ return CommandType.FunctionBegin, locals() | |
+ | |
+ | |
+def BFFFunctionEnd(): | |
+ return CommandType.FunctionEnd, locals() | |
+ | |
+ | |
+def BFFAssignment(var_name, expr): | |
+ return CommandType.Assignment, locals() | |
+ | |
+ | |
+def BFFConcatenation(var_name, expr): | |
+ return CommandType.Concatenation, locals() | |
+ | |
+ | |
+def PushCommand(bff, command): | |
+ bff.commands.append(command) | |
+ | |
+ | |
+def PushHardcodedPrologue(bff): | |
+ PushCommand(bff, BFFRaw(HARDCODED_PROLOGUE)) | |
+ | |
+ | |
+def PushHeadingComment(bff, message): | |
+ if len(bff.commands) > 0: | |
+ PushCommand(bff, BFFRaw('\n')) | |
+ lines = message.splitlines() | |
+ PushCommand(bff, BFFRaw(';; # %s\n' % lines[0])) | |
+ for line in lines[1:]: | |
+ PushCommand(bff, BFFRaw(';; %s\n' % line)) | |
+ | |
+ | |
+def PushComment(bff, message): | |
+ for line in message.splitlines(): | |
+ PushCommand(bff, BFFIndentedRaw(';; %s\n' % line)) | |
+ | |
+ | |
+def BFFBlock(bff, begin_command): | |
+ class ContextManager: | |
+ | |
+ def __enter__(self): | |
+ PushCommand(bff, begin_command) | |
+ | |
+ def __exit__(self, exc_type, exc_value, traceback): | |
+ PushCommand(bff, BFFFunctionEnd()) | |
+ return ContextManager() | |
+ | |
+ | |
+def BFFStruct(commands): | |
+ return CommandType.Struct, locals() | |
+ | |
+ | |
+def MakeToolset(flavor, generator_flags): | |
+ class Toolset: | |
+ def __init__(self): | |
+ self.path_from_arch = {} | |
+ | |
+ toolset = Toolset() | |
+ if flavor == 'win': | |
+ def OpenOutput(__ignored_path, __ignored_mode): | |
+ class NullFile: | |
+ def write(self,_): | |
+ pass | |
+ def close(self): | |
+ pass | |
+ return NullFile() | |
+ | |
+ shared_system_includes = [] | |
+ toplevel_build = '' | |
+ cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles( | |
+ toplevel_build, generator_flags, shared_system_includes, OpenOutput) | |
+ for arch, path in sorted(cl_paths.iteritems()): | |
+ toolset.path_from_arch[arch] = os.path.dirname(path) | |
+ | |
+ return toolset | |
+ | |
+def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
+ if flavor == 'mac': | |
+ class MacPlatform: | |
+ | |
+ def __init__(self): | |
+ self.xcode_settings = gyp.xcode_emulation.XcodeSettings( | |
+ target_spec) | |
+ | |
+ def get_dll_extension(self): | |
+ return '.dylib' | |
+ | |
+ def get_path(self, config_name): | |
+ return None | |
+ | |
+ def linker_options_for_dll(self): | |
+ return '-shared' | |
+ | |
+ def push_specific_cflags(self, options, config_name): | |
+ options += self.xcode_settings.GetCflags(config_name) | |
+ | |
+ def push_include_dir(self, options, include_dir): | |
+ options.append('-I') | |
+ options.append('"%s"' % include_dir) | |
+ | |
+ def push_define(self, options, flag, value=None): | |
+ options.append('-D') | |
+ if value is not None: | |
+ options.append('%s="%s"' % (flag, value)) | |
+ else: | |
+ options.append('%s' % flag) | |
+ | |
+ def ldflags(self, libraries): | |
+ return self.xcode_settings.AdjustLibraries(libraries) | |
+ | |
+ def ldflags_for_library_dirs(self, library_dirs): | |
+ flags = [] | |
+ for library_dir in library_dirs: | |
+ flags += ['-L"%s"' % library_dir] | |
+ flags += ['-F"%s"' % library_dir] | |
+ return flags | |
+ | |
+ return MacPlatform() | |
+ elif flavor == 'win': | |
+ class WinPlatform: | |
+ def __init__(self): | |
+ self.msvs_settings = gyp.msvs_emulation.MsvsSettings(target_spec, generator_flags) | |
+ | |
+ def get_dll_extension(self): | |
+ return '.dll' | |
+ | |
+ def get_path(self, config_name): | |
+ arch = self.msvs_settings.GetArch(config_name) | |
+ return toolset.path_from_arch[arch] | |
+ | |
+ def push_specific_cflags(self, options, config_name): | |
+ options += self.msvs_settings.GetCflags(config_name) | |
+ | |
+ def push_define(self, options, flag, value=None): | |
+ if value is not None: | |
+ options.append('/D%s="%s"' % (flag, value)) | |
+ else: | |
+ options.append('/D%s' % flag) | |
+ | |
+ def push_include_dir(self, options, include_dir): | |
+ options.append('/I"%s"' % include_dir) | |
+ | |
+ def linker_options_for_dll(self): | |
+ return '/DLL' | |
+ | |
+ def ldflags(self, libraries): | |
+ return self.msvs_settings.AdjustLibraries(libraries) | |
+ | |
+ def ldflags_for_library_dirs(self, library_dirs): | |
+ flags = [] | |
+ for library_dir in library_dirs: | |
+ flags += ['/LIBPATH:"%s"' % library_dir] | |
+ return flags | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ return WinPlatform() | |
+ else: | |
+ raise ValueError('Unimplemented flavor %s' % flavor) | |
+ | |
+ | |
+def MakeCompilerOptions(platform): | |
+ return [] | |
+ | |
+ | |
+def CopyCompilerOptions(compiler_options): | |
+ result = copy.copy(compiler_options) | |
+ return result | |
+ | |
+ | |
+def CompilerOptionsPushDefine(platform, compiler_options, flag, value=None): | |
+ platform.push_define(compiler_options, flag, value) | |
+ | |
+ | |
+def CompilerOptionsPushIncludeDir(platform, compiler_options, include_dir): | |
+ platform.push_include_dir(compiler_options, include_dir) | |
+ | |
+ | |
+def CompilerOptionsHasCommandLine(compiler_options): | |
+ return len(compiler_options) > 0 | |
+ | |
+ | |
+def CompilerOptionsConfigure(platform, compiler_options, config_name): | |
+ # TODO(nicolas): take language into account | |
+ platform.push_specific_cflags(compiler_options, config_name) | |
+ | |
+ | |
+def CompilerOptionsGetCommandLine(compiler_options): | |
+ return ' '.join(compiler_options) | |
+ | |
+ | |
+def PlatformLinkerOptionsForDLL(platform): | |
+ return platform.linker_options_for_dll() | |
+ | |
+def PlatformLDFlagsForLibraryDirs(platform, library_dirs): | |
+ return platform.ldflags_for_library_dirs(library_dirs) | |
+ | |
+ | |
+def PlatformLDFlagsForLibraries(platform, libraries): | |
+ return platform.ldflags(libraries) | |
+ | |
+def PlatformGetToolsetPaths(platform, config_name): | |
+ return [platform.get_path(config_name)] | |
+ | |
+def PlatformGetDLLExtension(platform): | |
+ return platform.get_dll_extension() | |
+ | |
+# TODO(nil): evaluate the gyp variables correctly. | |
+# see usage of GypPathToNinja in ninja.py for reference (and discussion around it) | |
+# return bff targets produced to caller (useful to create aliases) | |
+ | |
+ | |
+def PushTargetSpecs( | |
+ bff, | |
+ platform, | |
+ inputs_base_dir, | |
+ qualified_target, | |
+ target_spec, | |
+ target_dicts, | |
+ config_names | |
+): | |
+ target_build_file, target_name, target_toolset = \ | |
+ gyp.common.ParseQualifiedTarget(qualified_target) | |
+ | |
+ dependencies = target_spec.get('dependencies', []) | |
+ if not inputs_base_dir: | |
+ inputs_base_dir = '.' | |
+ | |
+ linkable_types = set(['static_library', 'shared_library']) | |
+ all_bff_target_names = [] | |
+ linkable_dependencies = [] | |
+ other_dependencies = [] | |
+ for d_qualified_target in dependencies: | |
+ if target_dicts[d_qualified_target]['type'] in linkable_types: | |
+ linkable_dependencies.append(d_qualified_target) | |
+ else: | |
+ other_dependencies.append(d_qualified_target) | |
+ | |
+ for config_name in config_names: | |
+ def bff_target_from_gyp_target(qualified_target, config_name): | |
+ return '%s-%s' % (qualified_target, config_name) | |
+ | |
+ ## | |
+ # NOTE([email protected]): | |
+ # We are appending the "config" after the target name. We make | |
+ # this initial choice which means we have all configs in the same bff at the | |
+ # expense of making it slightly more complicated. | |
+ # | |
+ # The advantage of the many configs in one bff is ease of building Debug and Release with | |
+ # the same project file. | |
+ bff_target_name = bff_target_from_gyp_target( | |
+ qualified_target, config_name) | |
+ bff_linkable_dependencies = [] | |
+ for d_qualified_target in linkable_dependencies: | |
+ bff_linkable_dependencies.append( | |
+ bff_target_from_gyp_target( | |
+ d_qualified_target, | |
+ config_name | |
+ ) | |
+ ) | |
+ | |
+ bff_other_dependencies = [] | |
+ for d_qualified_target in other_dependencies: | |
+ d_bff_target_name = '%s' % bff_target_from_gyp_target( | |
+ d_qualified_target, | |
+ config_name | |
+ ) | |
+ if not BFFIsEmptyTarget(bff, d_bff_target_name): | |
+ bff_other_dependencies.append(d_bff_target_name) | |
+ | |
+ if PushTargetForConfig( | |
+ bff, | |
+ platform, | |
+ inputs_base_dir, | |
+ target_spec, | |
+ config_name, | |
+ bff_target_name, | |
+ bff_linkable_dependencies, | |
+ bff_other_dependencies): | |
+ # Easier name for the target | |
+ bff_alias_name = '%s-%s' % (target_name, config_name) | |
+ PushAlias(bff, bff_alias_name, [bff_target_name]) | |
+ all_bff_target_names.append(bff_target_name) | |
+ else: | |
+ # A target w/o a corresponding FB correspondant is a empty target | |
+ # That we'd like to remember | |
+ BFFRegisterEmptyTarget(bff, bff_target_name) | |
+ | |
+ if not all_bff_target_names: | |
+ PushComment(bff, "Nothing to generate for %s" % target_name) | |
+ else: | |
+ PushAlias(bff, target_name, all_bff_target_names) | |
+ | |
+ | |
+def PushTargetForConfig( | |
+ bff, | |
+ platform, | |
+ inputs_base_dir, | |
+ target_spec, | |
+ config_name, | |
+ bff_target_name, | |
+ bff_linkable_dependencies, | |
+ bff_other_dependencies): | |
+ target_toolset = target_spec['toolset'] | |
+ target_name = target_spec['target_name'] | |
+ | |
+ def gyp_path_to_os_path(gyp_path): | |
+ fragments = gyp_path.split('/') | |
+ return os.path.abspath( | |
+ os.path.join( | |
+ inputs_base_dir, | |
+ os.path.join(*fragments) | |
+ ) | |
+ ) | |
+ | |
+ def using_config(): | |
+ PushCommand( | |
+ bff, | |
+ BFFUsing(GetBFFConfigVariableName(target_toolset, config_name)) | |
+ ) | |
+ | |
+ def using_compiler(toolset, config_name, compiler_type): | |
+ PushCommand( | |
+ bff, | |
+ BFFUsing( | |
+ '.%s%s%sCompiler' % | |
+ (toolset.capitalize(), | |
+ config_name.capitalize(), | |
+ compiler_type))) | |
+ | |
+ bff_action_target_names = [] | |
+ # copies targets are shared across configs: | |
+ for copy_index, copy in enumerate(target_spec.get('copies', [])): | |
+ copy_name = '%s-Copy-%s' % (bff_target_name, copy_index) | |
+ with BFFBlock(bff, BFFFunctionBegin('Copy', [copy_name])): | |
+ using_config() | |
+ PushAssignment(bff, | |
+ '.Source', | |
+ # TODO(nicolas): gyp_path_to_os_path -> | |
+ # os_path_from_gyp_path | |
+ map(gyp_path_to_os_path, copy['files']) | |
+ ) | |
+ destination = gyp_path_to_os_path(copy['destination']) | |
+ destination = os.path.join(destination, '') | |
+ PushAssignment(bff, '.Dest', destination) | |
+ bff_action_target_names.append(copy_name) | |
+ | |
+ def push_single_output_action(action_spec, prepended_inputs=None): | |
+ if prepended_inputs is None: | |
+ prepended_inputs = [] | |
+ if len(action_spec['outputs']) > 1: | |
+ raise ValueError( | |
+ "More than 1 output (action_name: %s) not supported" % | |
+ action_spec['action_name']) | |
+ action = action_spec['action'] | |
+ # TODO(nicolas): FASTbuild wants the actual executable location | |
+ # TODO(nicolas): look in toolset path too | |
+ executable = shell_which(action[0], PlatformGetToolsetPaths(platform, config_name)) | |
+ PushAssignment(bff, '.ExecExecutable', executable) | |
+ # TODO(nicolas): hardcoded platform quoting | |
+ quoted_arguments = ['"%s"' % x for x in action[1:]] | |
+ PushAssignment( | |
+ bff, '.ExecArguments', ' '.join(quoted_arguments)) | |
+ PushAssignment( | |
+ bff, | |
+ '.ExecInput', | |
+ prepended_inputs + | |
+ action_spec.get( | |
+ 'inputs', | |
+ [])) | |
+ output = gyp_path_to_os_path(action_spec['outputs'][0]) | |
+ PushAssignment(bff, '.ExecOutput', output) | |
+ | |
+ def compiler_type_for_source(source): | |
+ _ignored_name, ext = os.path.splitext(source) | |
+ if ext in compiler_type_extension_index: | |
+ compiler_type = compiler_type_extension_index[ext] | |
+ return compiler_type | |
+ | |
+ def is_compilable(source): | |
+ compiler_type = compiler_type_for_source(source) | |
+ return compiler_type and compiler_type != CompilerType.NONE | |
+ | |
+ def apply_multiple_output_hack(action_spec): | |
+ # TODO(nicolas): TAG(hack) workaround for multiple generated files | |
+ if len(action_spec['outputs']) > 1: | |
+ # TODO(nicolas): also of interests are the linkable products, like | |
+ # dylibs | |
+ compilable_sources = [ | |
+ y for y in action_spec['outputs'] if is_compilable(y)] | |
+ unique_output = None | |
+ if len(compilable_sources) == 1: | |
+ unique_output = compilable_sources[0] | |
+ else: | |
+ unique_output = action_spec['outputs'][0] | |
+ | |
+ if unique_output: | |
+ PushComment( | |
+ bff, | |
+ 'TAG(hack) had to ignore extra outputs %s because Exec does not support multiple outputs, see URL(https://github.com/fastbuild/fastbuild/issues/95)' % | |
+ action_spec['outputs'][ | |
+ 1:]) | |
+ action_spec['outputs'] = [unique_output] | |
+ print 'Applied workaround for %s (Exec missing multiple outputs)' % action_name | |
+ | |
+ source_generators = defaultdict(list) | |
+ for action_spec in target_spec.get('actions', []): | |
+ action_name = action_spec['action_name'] | |
+ bff_action_name = '%s-%s' % (bff_target_name, action_name) | |
+ PushComment(bff, 'Action %s' % action_name) | |
+ | |
+ apply_multiple_output_hack(action_spec) | |
+ | |
+ with BFFBlock(bff, BFFFunctionBegin('Exec', [bff_action_name])): | |
+ using_config() | |
+ push_single_output_action(action_spec) | |
+ | |
+ output_path = action_spec['outputs'][0] | |
+ compiler_type = compiler_type_for_source(output_path) | |
+ if compiler_type: | |
+ source_generators[compiler_type].append(bff_action_name) | |
+ else: | |
+ bff_action_target_names.append(bff_action_name) | |
+ | |
+ # rules produce actions: | |
+ for rule in target_spec.get('rules', []): | |
+ rule_name = rule['rule_name'] | |
+ PushComment(bff, 'Rule %s' % rule_name) | |
+ rule_inputs = rule.get('inputs', []) | |
+ rule_extension = '.%s' % rule['extension'] | |
+ action_index = 0 | |
+ for gyp_source in target_spec.get('sources', []): | |
+ os_path = gyp_path_to_os_path(gyp_source) | |
+ name, ext = os.path.splitext(os_path) | |
+ if ext == rule_extension: | |
+ PushComment( | |
+ bff, 'Action for %s' % gyp_source) | |
+ bff_action_name = '%s-%s-%s' % (bff_target_name, | |
+ rule_name, action_index) | |
+ action_index += 1 | |
+ action = rule.get('action', []) | |
+ with BFFBlock(bff, BFFFunctionBegin('Exec', [bff_action_name])): | |
+ using_config() | |
+ PushAssignment( | |
+ bff, '.RuleInputDirname', os.path.dirname(name)) | |
+ PushAssignment( | |
+ bff, '.RuleInputName', os.path.basename(os_path)) | |
+ PushAssignment( | |
+ bff, '.RuleInputRoot', os.path.basename(name)) | |
+ PushAssignment(bff, '.RuleInputExt', ext) | |
+ push_single_output_action(rule, [os_path]) | |
+ | |
+ rule_output = rule['outputs'][0] | |
+ if rule.get('process_outputs_as_sources', False): | |
+ compiler_type = compiler_type_for_source(rule_output) | |
+ if compiler_type: | |
+ source_generators[compiler_type].append( | |
+ bff_action_name) | |
+ else: | |
+ PushComment(bff, 'Unknown generated file: %s' % | |
+ rule_output) | |
+ else: | |
+ # TODO(nicolas): actually these actions are meant to be | |
+ # pre-requisite to us, and should not be fed into the | |
+ # alias that stands for our products. Otherwise we trigger | |
+ # the execution of the wrong actions at the wrong time | |
+ # when triggering the main target alias | |
+ bff_action_target_names.append(bff_action_name) | |
+ | |
+ sources_by_compiler_type = defaultdict(list) | |
+ for gyp_source in target_spec.get('sources', []): | |
+ compiler_type = compiler_type_for_source(gyp_source) | |
+ if compiler_type: | |
+ sources_by_compiler_type[compiler_type].append( | |
+ gyp_path_to_os_path(gyp_source) | |
+ ) | |
+ else: | |
+ PushComment(bff, 'Unknown compiler type for file: %s' % gyp_source) | |
+ | |
+ def push_compiler_sources( | |
+ os_sources, | |
+ compiler_type, | |
+ target_spec, | |
+ config_name, | |
+ dependencies): | |
+ using_compiler(target_toolset, config_name, compiler_type) | |
+ | |
+ PushAssignment(bff, '.CompilerInputFiles', os_sources) | |
+ compiler_options = MakeCompilerOptions(platform) | |
+ CompilerOptionsConfigure(platform, compiler_options, config_name) | |
+ if CompilerOptionsHasCommandLine(compiler_options): | |
+ # TODO(nicolas): I wonder if we could de-duplicate those | |
+ # into structs, for more readability | |
+ PushComment(bff, 'Config-specific command line') | |
+ PushCommand( | |
+ bff, | |
+ BFFConcatenation( | |
+ '.CompilerOptions', | |
+ ' ' + CompilerOptionsGetCommandLine(compiler_options), | |
+ ) | |
+ ) | |
+ | |
+ config = target_spec.get('configurations').get(config_name, {}) | |
+ compiler_options = MakeCompilerOptions(platform) | |
+ for define in config.get('defines', []): | |
+ CompilerOptionsPushDefine( | |
+ platform, | |
+ compiler_options, | |
+ define | |
+ ) | |
+ for include_dir in config.get('include_dirs', []): | |
+ CompilerOptionsPushIncludeDir( | |
+ platform, | |
+ compiler_options, | |
+ include_dir if os.path.isabs( | |
+ include_dir) else gyp_path_to_os_path(include_dir) | |
+ ) | |
+ if CompilerOptionsHasCommandLine(compiler_options): | |
+ PushComment( | |
+ bff, | |
+ 'Target-specific command line' | |
+ ) | |
+ PushCommand( | |
+ bff, | |
+ BFFConcatenation( | |
+ '.CompilerOptions', | |
+ ' ' + CompilerOptionsGetCommandLine(compiler_options) | |
+ ) | |
+ ) | |
+ PushAssignment( | |
+ bff, | |
+ '.CompilerOutputPath', | |
+ '$ProductDir$/obj/%s' % | |
+ target_name) | |
+ | |
+ config = target_spec.get('configurations').get(config_name, {}) | |
+ | |
+ # Generate compilation targets: | |
+ bff_all_object_list_names = [] | |
+ all_compilation_sources = set( | |
+ sources_by_compiler_type.keys() + | |
+ source_generators.keys()) | |
+ if fastbuild_verbose: | |
+ PushComment(bff, 'Sources by compiler\n%s' % | |
+ pformat(all_compilation_sources)) | |
+ | |
+ for compiler_type in all_compilation_sources: | |
+ if compiler_type == CompilerType.NONE: | |
+ continue | |
+ sources = ( | |
+ sources_by_compiler_type.get(compiler_type, []) + | |
+ source_generators.get(compiler_type, []) | |
+ ) | |
+ bff_object_list_name = "%s-Objects-%s" % ( | |
+ bff_target_name, compiler_type) | |
+ with BFFBlock( | |
+ bff, | |
+ BFFFunctionBegin('ObjectList', [bff_object_list_name]) | |
+ ): | |
+ using_config() | |
+ push_compiler_sources( | |
+ sources, | |
+ compiler_type, | |
+ target_spec, config_name, | |
+ bff_other_dependencies) | |
+ bff_all_object_list_names.append(bff_object_list_name) | |
+ | |
+ # Generate link targets: | |
+ bff_product_target_name = None | |
+ bff_executable_libraries = [] | |
+ if ( | |
+ target_spec['type'] == 'executable' or | |
+ target_spec['type'] == 'shared_library' | |
+ ): | |
+ # TODO(nicolas): simplify | |
+ linker_target_name = '%s-Exe' % bff_target_name | |
+ # TODO(nicolas): TAG(platform) this depends on the target platform | |
+ linker_product_suffix = dict( | |
+ executable='.exe', | |
+ shared_library=PlatformGetDLLExtension(platform) | |
+ )[target_spec['type']] | |
+ linker_product = "%s%s" % ( | |
+ target_spec['product_name'], linker_product_suffix) | |
+ linker_function = dict( | |
+ executable='Executable', | |
+ shared_library='DLL', | |
+ )[target_spec['type']] | |
+ with BFFBlock(bff, BFFFunctionBegin(linker_function, [linker_target_name])): | |
+ using_config() | |
+ PushAssignment( | |
+ bff, '.LinkerOutput', '$ProductDir$/%s' % linker_product | |
+ ) | |
+ if linker_function == 'DLL': | |
+ # NOTE(nil): TAG(platform) the right flags are | |
+ # necessary. DLL nodes in fbuild don't actually do | |
+ # anything special with linker flags, but forgetting | |
+ # them will lead FBuild to consider the node an | |
+ # executable rather than a DLL, and it will then | |
+ # complain during dependency checking. | |
+ PushCommand( | |
+ bff, | |
+ BFFConcatenation( | |
+ '.LinkerOptions', | |
+ ' %s' % PlatformLinkerOptionsForDLL(platform) | |
+ ) | |
+ ) | |
+ | |
+ for flag in PlatformLDFlagsForLibraryDirs( | |
+ platform, config.get('library_dirs', [])): | |
+ PushCommand( | |
+ bff, | |
+ BFFConcatenation( | |
+ '.LinkerOptions', | |
+ ' %s' % flag | |
+ ) | |
+ ) | |
+ for flag in PlatformLDFlagsForLibraries( | |
+ platform, target_spec.get('libraries', [])): | |
+ PushCommand( | |
+ bff, | |
+ BFFConcatenation( | |
+ '.LinkerOptions', | |
+ ' %s' % flag | |
+ ) | |
+ ) | |
+ | |
+ PushAssignment( | |
+ bff, | |
+ '.Libraries', | |
+ bff_linkable_dependencies + | |
+ bff_executable_libraries + | |
+ bff_all_object_list_names | |
+ ) | |
+ bff_product_target_name = linker_target_name | |
+ elif target_spec['type'] == 'static_library': | |
+ library_name = '%s-Lib' % bff_target_name | |
+ with BFFBlock(bff, BFFFunctionBegin('Library', [library_name])): | |
+ using_config() | |
+ using_compiler(target_toolset, config_name, CompilerType.CXX) | |
+ | |
+ # TODO(nil): change, this shouldnt be hardcoded | |
+ PushAssignment( | |
+ bff, | |
+ '.LibrarianOutput', | |
+ '$ProductDir$/Lib%s.lib' % | |
+ target_spec['product_name']) | |
+ PushAssignment(bff, | |
+ '.LibrarianAdditionalInputs', | |
+ bff_all_object_list_names) | |
+ PushAssignment(bff, | |
+ '.CompilerOutputPath', | |
+ '$ProductDir$/lib/%s' % target_name) | |
+ | |
+ bff_product_target_name = library_name | |
+ elif target_spec['type'] == 'none': | |
+ # Nothing compilable there | |
+ pass | |
+ else: | |
+ PushComment( | |
+ bff, "don't know how to generate target %s" % bff_target_name) | |
+ | |
+ subtarget_names = [] | |
+ subtarget_names += bff_action_target_names | |
+ if bff_product_target_name: | |
+ subtarget_names += [bff_product_target_name] | |
+ | |
+ if subtarget_names: | |
+ BFFRegisterTarget(bff, bff_target_name) | |
+ PushAlias(bff, bff_target_name, | |
+ subtarget_names + bff_other_dependencies) | |
+ return True | |
+ else: | |
+ return False | |
+ | |
+ | |
+def BFFRegisterTarget(bff, fastbuild_target_name): | |
+ debug_assert(fastbuild_target_name not in | |
+ bff.all_fb_targets + bff.all_empty_targets) | |
+ bff.all_fb_targets.append(fastbuild_target_name) | |
+ | |
+ | |
+def BFFRegisterEmptyTarget(bff, fastbuild_target_name): | |
+ debug_assert(fastbuild_target_name not in | |
+ bff.all_fb_targets + bff.all_empty_targets) | |
+ bff.all_empty_targets.append(fastbuild_target_name) | |
+ | |
+ | |
+def BFFIsEmptyTarget(bff, fastbuild_target_name): | |
+ return fastbuild_target_name in bff.all_empty_targets | |
+ | |
+ | |
+def PushAssignment(bff, key, value): | |
+ PushCommand(bff, BFFAssignment(key, value)) | |
+ | |
+ | |
+def PushAlias(bff, alias_name, targets): | |
+ with BFFBlock(bff, BFFFunctionBegin('Alias', [alias_name])): | |
+ PushCommand(bff, | |
+ BFFAssignment( | |
+ '.Targets', | |
+ targets | |
+ ) | |
+ ) | |
+ | |
+ | |
+def BFFFinalize(bff): | |
+ PushHeadingComment(bff, 'This is the global, default target') | |
+ PushAlias(bff, 'All', bff.all_fb_targets) | |
+ | |
+ | |
+def BFFWriteToFile(bff, bff_path): | |
+ print "Writing to file %s" % bff_path | |
+ bff_dir = os.path.dirname(bff_path) | |
+ if len(bff_dir) and not os.path.exists(bff_dir): | |
+ os.makedirs(bff_dir) | |
+ | |
+ class Indentation: | |
+ | |
+ def __init__(self): | |
+ self.current = 0 | |
+ self.increment = 4 | |
+ | |
+ def inc(self): | |
+ self.current += self.increment | |
+ | |
+ def dec(self): | |
+ self.current -= self.increment | |
+ | |
+ def write(self, dest): | |
+ dest.write(' ' * self.current) | |
+ | |
+ indent = Indentation() | |
+ current_block = None | |
+ | |
+ def write_string_list(dest, list_value): | |
+ if len(list_value) == 0: | |
+ return | |
+ # TODO(nil): wrap when too long | |
+ dest.write("'%s'" % list_value[0]) | |
+ for e in list_value[1:]: | |
+ dest.write(', ') | |
+ dest.write("'%s'" % e) | |
+ | |
+ def write_struct(bff_file, struct_data): | |
+ bff_file.write('\n') | |
+ indent.write(bff_file) | |
+ bff_file.write('[\n') | |
+ indent.inc() | |
+ for command_type, command_data in struct_data['commands']: | |
+ if command_type == CommandType.Raw: | |
+ bff_file.write(command_data) | |
+ elif command_type == CommandType.IndentedRaw: | |
+ indent.write(bff_file) | |
+ bff_file.write(command_data) | |
+ elif command_type == CommandType.Assignment: | |
+ write_variable_op(bff_file, command_data, '=') | |
+ elif command_type == CommandType.Using: | |
+ indent.write(bff_file) | |
+ bff_file.write('Using(%s)\n' % command_data['variable_name']) | |
+ else: | |
+ assert false, "invalid command type %s" % command_type | |
+ indent.dec() | |
+ indent.write(bff_file) | |
+ bff_file.write(']') | |
+ | |
+ def write_variable_op(bff_file, command_data, op): | |
+ indent.write(bff_file) | |
+ bff_file.write(command_data['var_name']) | |
+ bff_file.write(' %s ' % op) | |
+ if isinstance(command_data['expr'], list): | |
+ list_value = command_data['expr'] | |
+ bff_file.write('{') | |
+ write_string_list(bff_file, list_value) | |
+ bff_file.write('}') | |
+ elif command_data['expr'][0] == CommandType.Struct: | |
+ write_struct(bff_file, command_data['expr'][1]) | |
+ else: | |
+ bff_file.write("'%s'" % command_data['expr']) | |
+ bff_file.write('\n') | |
+ | |
+ with open(bff_path, 'w') as bff_file: | |
+ for command_type, command_data in bff.commands: | |
+ if command_type == CommandType.Raw: | |
+ bff_file.write(command_data) | |
+ elif command_type == CommandType.IndentedRaw: | |
+ indent.write(bff_file) | |
+ bff_file.write(command_data) | |
+ elif command_type == CommandType.FunctionBegin: | |
+ current_block = command_data['function_name'] | |
+ indent.write(bff_file) | |
+ bff_file.write("%s(" % command_data['function_name']) | |
+ write_string_list(bff_file, command_data['function_arguments']) | |
+ bff_file.write(")\n") | |
+ bff_file.write("{\n") | |
+ indent.inc() | |
+ elif command_type == CommandType.FunctionEnd: | |
+ indent.dec() | |
+ indent.write(bff_file) | |
+ bff_file.write('}\n') | |
+ elif command_type == CommandType.Assignment: | |
+ write_variable_op(bff_file, command_data, '=') | |
+ elif command_type == CommandType.Concatenation: | |
+ write_variable_op(bff_file, command_data, '+') | |
+ elif command_type == CommandType.Using: | |
+ indent.write(bff_file) | |
+ bff_file.write('Using(%s)\n' % command_data['variable_name']) | |
+ else: | |
+ assert false, "unknown command type %s" % command_type | |
+ | |
+ | |
+class BFFCreator: | |
+ | |
+ def __init__(self): | |
+ self.commands = [] | |
+ self.all_fb_targets = [] | |
+ self.all_empty_targets = [] | |
+ | |
+HARDCODED_PROLOGUE = r""" | |
+#include "local.bff" | |
+""" | |
-- | |
2.7.0.windows.1 | |
From 3dba691888124a267d2e7351beb8c6c73c036c00 Mon Sep 17 00:00:00 2001 | |
From: nil <[email protected]> | |
Date: Mon, 20 Jun 2016 14:36:56 +0200 | |
Subject: [PATCH 02/19] Escape defines as shell arguments should | |
Since we were not escaping double-quotes, you could not define a string | |
using a 'define' directive. | |
--- | |
pylib/gyp/generator/fastbuild.py | 34 +++++++++++++++++----------------- | |
1 file changed, 17 insertions(+), 17 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 8cdf52c..974b580 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -12,6 +12,7 @@ import gyp.common | |
import gyp.msvs_emulation | |
import gyp.xcode_emulation | |
import os | |
+import re | |
from collections import defaultdict | |
from itertools import product | |
@@ -371,12 +372,9 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
options.append('-I') | |
options.append('"%s"' % include_dir) | |
- def push_define(self, options, flag, value=None): | |
+ def push_define(self, options, flag): | |
options.append('-D') | |
- if value is not None: | |
- options.append('%s="%s"' % (flag, value)) | |
- else: | |
- options.append('%s' % flag) | |
+ options.append('%s' % flag) | |
def ldflags(self, libraries): | |
return self.xcode_settings.AdjustLibraries(libraries) | |
@@ -390,6 +388,12 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
return MacPlatform() | |
elif flavor == 'win': | |
+ def QuoteShellArgument(arg): | |
+ # NOTE(nil): adapted from ninja.py | |
+ if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg): | |
+ return arg # No quoting necessary. | |
+ return gyp.msvs_emulation.QuoteForRspFile(arg) | |
+ | |
class WinPlatform: | |
def __init__(self): | |
self.msvs_settings = gyp.msvs_emulation.MsvsSettings(target_spec, generator_flags) | |
@@ -404,14 +408,14 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
def push_specific_cflags(self, options, config_name): | |
options += self.msvs_settings.GetCflags(config_name) | |
- def push_define(self, options, flag, value=None): | |
- if value is not None: | |
- options.append('/D%s="%s"' % (flag, value)) | |
- else: | |
- options.append('/D%s' % flag) | |
+ def push_define(self, options, flag): | |
+ argument = '/D%s' % flag | |
+ # NOTE(nil): cl.exe interprets # as = | |
+ argument.replace('#', '\\%03o' % ord('#')) | |
+ options.append(QuoteShellArgument(argument)) | |
def push_include_dir(self, options, include_dir): | |
- options.append('/I"%s"' % include_dir) | |
+ options.append(QuoteShellArgument('/I"%s"' % include_dir)) | |
def linker_options_for_dll(self): | |
return '/DLL' | |
@@ -425,10 +429,6 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
flags += ['/LIBPATH:"%s"' % library_dir] | |
return flags | |
- | |
- | |
- | |
- | |
return WinPlatform() | |
else: | |
raise ValueError('Unimplemented flavor %s' % flavor) | |
@@ -443,8 +443,8 @@ def CopyCompilerOptions(compiler_options): | |
return result | |
-def CompilerOptionsPushDefine(platform, compiler_options, flag, value=None): | |
- platform.push_define(compiler_options, flag, value) | |
+def CompilerOptionsPushDefine(platform, compiler_options, flag): | |
+ platform.push_define(compiler_options, flag) | |
def CompilerOptionsPushIncludeDir(platform, compiler_options, include_dir): | |
-- | |
2.7.0.windows.1 | |
From 34098c204bedb15a50046e0c63602ca92e84f997 Mon Sep 17 00:00:00 2001 | |
From: nil <[email protected]> | |
Date: Mon, 20 Jun 2016 16:50:18 +0200 | |
Subject: [PATCH 03/19] Correct linking | |
- support loadable_module | |
- append linker flags as generated by the MSVS emulation module | |
--- | |
pylib/gyp/generator/fastbuild.py | 137 ++++++++++++++++++++++++++++++--------- | |
1 file changed, 108 insertions(+), 29 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 974b580..c64f81d 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -5,6 +5,7 @@ | |
# URL(http://fastbuild.org) opensource build system developed by Franta Fulin. | |
# | |
# TODO(nicolas): $(IntDir) MSVS macro to be supported. (Or not?) | |
+# TODO(nicolas): autopep8, add double spaces after top level functions | |
import copy | |
import gyp | |
@@ -350,6 +351,11 @@ def MakeToolset(flavor, generator_flags): | |
def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
if flavor == 'mac': | |
+ def QuoteShellArgument(arg): | |
+ # NOTE(nil): adapted from ninja.py | |
+ if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg): | |
+ return arg # No quoting necessary. | |
+ return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'" | |
class MacPlatform: | |
def __init__(self): | |
@@ -366,7 +372,9 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
return '-shared' | |
def push_specific_cflags(self, options, config_name): | |
- options += self.xcode_settings.GetCflags(config_name) | |
+ options.extend(self.xcode_settings.GetCflags(config_name)) | |
+ | |
+ # TODO(nicolas) mac version of push_specific_ldflags | |
def push_include_dir(self, options, include_dir): | |
options.append('-I') | |
@@ -376,6 +384,9 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
options.append('-D') | |
options.append('%s' % flag) | |
+ def quote_argument(self, argument): | |
+ return QuoteShellArgument(argument) | |
+ | |
def ldflags(self, libraries): | |
return self.xcode_settings.AdjustLibraries(libraries) | |
@@ -406,7 +417,7 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
return toolset.path_from_arch[arch] | |
def push_specific_cflags(self, options, config_name): | |
- options += self.msvs_settings.GetCflags(config_name) | |
+ options.extend(self.msvs_settings.GetCflags(config_name)) | |
def push_define(self, options, flag): | |
argument = '/D%s' % flag | |
@@ -417,9 +428,37 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
def push_include_dir(self, options, include_dir): | |
options.append(QuoteShellArgument('/I"%s"' % include_dir)) | |
+ def quote_argument(self, argument): | |
+ return QuoteShellArgument(argument) | |
+ | |
def linker_options_for_dll(self): | |
return '/DLL' | |
+ def push_specific_ldflags(self, options, inputs_base_dir, config_name, target_type, target_product_name): | |
+ # TODO(nicolas): remove those hardcoded sentinels and replace with correct | |
+ # stuff (I need to figure out what there are for) | |
+ def gyp_to_build_path(x): | |
+ return PlatformGetPathFromGypPath(self, inputs_base_dir, x) | |
+ def expand_special(path, product_dir=None): | |
+ return "EXPAND_SPECIAL(%s, %s)" % (path, product_dir) | |
+ manifest_base_name = "MANIFEST_BASE_NAME" | |
+ output_name = "$ProductDir$/%s" % target_product_name | |
+ is_executable = target_type == 'executable' | |
+ build_dir = "BUILD_DIR" | |
+ | |
+ ldflags,\ | |
+ intermediate_manifest,\ | |
+ manifest_files = self.msvs_settings.GetLdflags( | |
+ config_name, | |
+ gyp_to_build_path, | |
+ expand_special, | |
+ manifest_base_name, | |
+ output_name, | |
+ is_executable, | |
+ build_dir | |
+ ) | |
+ options.extend(ldflags) | |
+ | |
def ldflags(self, libraries): | |
return self.msvs_settings.AdjustLibraries(libraries) | |
@@ -464,13 +503,32 @@ def CompilerOptionsGetCommandLine(compiler_options): | |
return ' '.join(compiler_options) | |
-def PlatformLinkerOptionsForDLL(platform): | |
- return platform.linker_options_for_dll() | |
+def MakeLinkerOptions(platform): | |
+ return [] | |
+ | |
+def LinkerOptionsConfigure(platform, linker_options, inputs_base_dir, config_name, target_type, target_product_name): | |
+ if target_type != 'executable': | |
+ # NOTE(nil): TAG(platform) FASTbuild figures out the type of a | |
+ # node by the presence of certain linker flags. Therefore the | |
+ # right flags are necessary. Forgetting it shows up as a | |
+ # FASTbuild complaint during dependency checking. | |
+ linker_options.append(platform.linker_options_for_dll()) | |
+ platform.push_specific_ldflags(linker_options, inputs_base_dir, config_name, target_type, target_product_name) | |
+ | |
+ | |
+def LinkerOptionsHasCommandLine(linker_options): | |
+ return len(linker_options) > 0 | |
+ | |
+ | |
+def LinkerOptionsGetCommandLine(linker_options): | |
+ return ' '.join(linker_options) | |
+ | |
+# TODO(nicolas): what about moving that to linker options function | |
def PlatformLDFlagsForLibraryDirs(platform, library_dirs): | |
return platform.ldflags_for_library_dirs(library_dirs) | |
- | |
+# TODO(nicolas): what about moving that to linker options function | |
def PlatformLDFlagsForLibraries(platform, libraries): | |
return platform.ldflags(libraries) | |
@@ -480,6 +538,18 @@ def PlatformGetToolsetPaths(platform, config_name): | |
def PlatformGetDLLExtension(platform): | |
return platform.get_dll_extension() | |
+def PlatformQuoteArgument(platform, argument): | |
+ return platform.quote_argument(argument) | |
+ | |
+def PlatformGetPathFromGypPath(platform, inputs_base_dir, gyp_path): | |
+ fragments = gyp_path.split('/') | |
+ return os.path.abspath( | |
+ os.path.join( | |
+ inputs_base_dir, | |
+ os.path.join(*fragments) | |
+ ) | |
+ ) | |
+ | |
# TODO(nil): evaluate the gyp variables correctly. | |
# see usage of GypPathToNinja in ninja.py for reference (and discussion around it) | |
# return bff targets produced to caller (useful to create aliases) | |
@@ -580,13 +650,7 @@ def PushTargetForConfig( | |
target_name = target_spec['target_name'] | |
def gyp_path_to_os_path(gyp_path): | |
- fragments = gyp_path.split('/') | |
- return os.path.abspath( | |
- os.path.join( | |
- inputs_base_dir, | |
- os.path.join(*fragments) | |
- ) | |
- ) | |
+ return PlatformGetPathFromGypPath(platform, inputs_base_dir, gyp_path) | |
def using_config(): | |
PushCommand( | |
@@ -632,7 +696,10 @@ def PushTargetForConfig( | |
# TODO(nicolas): look in toolset path too | |
executable = shell_which(action[0], PlatformGetToolsetPaths(platform, config_name)) | |
PushAssignment(bff, '.ExecExecutable', executable) | |
- # TODO(nicolas): hardcoded platform quoting | |
+ # TODO(nicolas): correct this hardcoded platform quoting | |
+ # NOTE(nicolas): it's tricky. I tried using PlatformQuoteArgument however | |
+ # this would transform the %1 that fasbtuild uses. Maybe we should protect it somehow, | |
+ # quote the arguments, and replace it back to %1 as a last pass. | |
quoted_arguments = ['"%s"' % x for x in action[1:]] | |
PushAssignment( | |
bff, '.ExecArguments', ' '.join(quoted_arguments)) | |
@@ -741,6 +808,7 @@ def PushTargetForConfig( | |
# the execution of the wrong actions at the wrong time | |
# when triggering the main target alias | |
bff_action_target_names.append(bff_action_name) | |
+ PushComment(bff, 'End Rule %s' % rule_name) | |
sources_by_compiler_type = defaultdict(list) | |
for gyp_source in target_spec.get('sources', []): | |
@@ -843,40 +911,48 @@ def PushTargetForConfig( | |
# Generate link targets: | |
bff_product_target_name = None | |
bff_executable_libraries = [] | |
+ target_type = target_spec['type'] | |
if ( | |
- target_spec['type'] == 'executable' or | |
- target_spec['type'] == 'shared_library' | |
+ target_type == 'executable' or | |
+ target_type == 'shared_library' or | |
+ target_type == 'loadable_module' | |
): | |
# TODO(nicolas): simplify | |
linker_target_name = '%s-Exe' % bff_target_name | |
# TODO(nicolas): TAG(platform) this depends on the target platform | |
linker_product_suffix = dict( | |
executable='.exe', | |
- shared_library=PlatformGetDLLExtension(platform) | |
- )[target_spec['type']] | |
+ shared_library=PlatformGetDLLExtension(platform), | |
+ loadable_module=PlatformGetDLLExtension(platform), | |
+ )[target_type] | |
linker_product = "%s%s" % ( | |
target_spec['product_name'], linker_product_suffix) | |
linker_function = dict( | |
executable='Executable', | |
shared_library='DLL', | |
- )[target_spec['type']] | |
+ loadable_module='DLL', | |
+ )[target_type] | |
with BFFBlock(bff, BFFFunctionBegin(linker_function, [linker_target_name])): | |
using_config() | |
+ | |
PushAssignment( | |
bff, '.LinkerOutput', '$ProductDir$/%s' % linker_product | |
) | |
- if linker_function == 'DLL': | |
- # NOTE(nil): TAG(platform) the right flags are | |
- # necessary. DLL nodes in fbuild don't actually do | |
- # anything special with linker flags, but forgetting | |
- # them will lead FBuild to consider the node an | |
- # executable rather than a DLL, and it will then | |
- # complain during dependency checking. | |
+ linker_options = MakeLinkerOptions(platform) | |
+ LinkerOptionsConfigure( | |
+ platform, | |
+ linker_options, | |
+ inputs_base_dir, | |
+ config_name, | |
+ target_type, | |
+ target_spec['product_name'] | |
+ ) | |
+ if LinkerOptionsHasCommandLine(linker_options): | |
PushCommand( | |
bff, | |
BFFConcatenation( | |
'.LinkerOptions', | |
- ' %s' % PlatformLinkerOptionsForDLL(platform) | |
+ ' ' + LinkerOptionsGetCommandLine(linker_options) | |
) | |
) | |
@@ -907,7 +983,7 @@ def PushTargetForConfig( | |
bff_all_object_list_names | |
) | |
bff_product_target_name = linker_target_name | |
- elif target_spec['type'] == 'static_library': | |
+ elif target_type == 'static_library': | |
library_name = '%s-Lib' % bff_target_name | |
with BFFBlock(bff, BFFFunctionBegin('Library', [library_name])): | |
using_config() | |
@@ -927,12 +1003,15 @@ def PushTargetForConfig( | |
'$ProductDir$/lib/%s' % target_name) | |
bff_product_target_name = library_name | |
- elif target_spec['type'] == 'none': | |
+ elif target_type == 'none': | |
# Nothing compilable there | |
pass | |
else: | |
PushComment( | |
- bff, "don't know how to generate target %s" % bff_target_name) | |
+ bff, "don't know how to generate target %s (type: %s)" % ( | |
+ bff_target_name, target_type | |
+ ) | |
+ ) | |
subtarget_names = [] | |
subtarget_names += bff_action_target_names | |
-- | |
2.7.0.windows.1 | |
From 83ce56b175885544d99973b7a620112f609403d9 Mon Sep 17 00:00:00 2001 | |
From: nil <[email protected]> | |
Date: Mon, 20 Jun 2016 17:59:40 +0200 | |
Subject: [PATCH 04/19] TODOs and one fix | |
--- | |
pylib/gyp/generator/fastbuild.py | 16 +++++++++++++++- | |
1 file changed, 15 insertions(+), 1 deletion(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index c64f81d..a6e1595 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -6,6 +6,18 @@ | |
# | |
# TODO(nicolas): $(IntDir) MSVS macro to be supported. (Or not?) | |
# TODO(nicolas): autopep8, add double spaces after top level functions | |
+# | |
+# TODO(nicolas): the way the generator creates alias nodes injects | |
+# some unwanted dependencies onto the other dependents: | |
+# | |
+# If executable A depends on a dynamic library B. Should the node | |
+# alias for A also bring B in, this sounds dubious. It is even more so | |
+# when the dependency (say C) is not even linkable, such as a | |
+# generator dependency. | |
+# | |
+# Try removing the Aliases entirely. Would the generated bff file work | |
+# at all without them. And if not, why not? | |
+# | |
import copy | |
import gyp | |
@@ -356,6 +368,7 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg): | |
return arg # No quoting necessary. | |
return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'" | |
+ | |
class MacPlatform: | |
def __init__(self): | |
@@ -571,7 +584,7 @@ def PushTargetSpecs( | |
if not inputs_base_dir: | |
inputs_base_dir = '.' | |
- linkable_types = set(['static_library', 'shared_library']) | |
+ linkable_types = set(['static_library', 'shared_library', 'loadable_module']) | |
all_bff_target_names = [] | |
linkable_dependencies = [] | |
other_dependencies = [] | |
@@ -810,6 +823,7 @@ def PushTargetForConfig( | |
bff_action_target_names.append(bff_action_name) | |
PushComment(bff, 'End Rule %s' % rule_name) | |
+ | |
sources_by_compiler_type = defaultdict(list) | |
for gyp_source in target_spec.get('sources', []): | |
compiler_type = compiler_type_for_source(gyp_source) | |
-- | |
2.7.0.windows.1 | |
From 6f9c26546ce6c298189f7568d70b0824ea2f6831 Mon Sep 17 00:00:00 2001 | |
From: nil <[email protected]> | |
Date: Mon, 11 Jul 2016 11:42:40 +0200 | |
Subject: [PATCH 05/19] Prepend shell interpreter when command is composite | |
--- | |
pylib/gyp/generator/fastbuild.py | 30 ++++++++++++++++++++++++------ | |
1 file changed, 24 insertions(+), 6 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index a6e1595..4fe1397 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -441,6 +441,12 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
def push_include_dir(self, options, include_dir): | |
options.append(QuoteShellArgument('/I"%s"' % include_dir)) | |
+ def shell_executable(self): | |
+ return "cmd.exe" | |
+ | |
+ def shell_cli_arguments(self, command_string): | |
+ return ["/C", '"%s"' % command_string] | |
+ | |
def quote_argument(self, argument): | |
return QuoteShellArgument(argument) | |
@@ -545,6 +551,12 @@ def PlatformLDFlagsForLibraryDirs(platform, library_dirs): | |
def PlatformLDFlagsForLibraries(platform, libraries): | |
return platform.ldflags(libraries) | |
+def PlatformGetShellInterpreter(platform): | |
+ return platform.shell_executable() | |
+ | |
+def PlatformShellInterpreterCommandLineArgumentsFor(platform, command): | |
+ return platform.shell_cli_arguments(command) | |
+ | |
def PlatformGetToolsetPaths(platform, config_name): | |
return [platform.get_path(config_name)] | |
@@ -707,13 +719,19 @@ def PushTargetForConfig( | |
action = action_spec['action'] | |
# TODO(nicolas): FASTbuild wants the actual executable location | |
# TODO(nicolas): look in toolset path too | |
- executable = shell_which(action[0], PlatformGetToolsetPaths(platform, config_name)) | |
+ if len(action) == 1: | |
+ executable = PlatformGetShellInterpreter(platform) | |
+ quoted_arguments = PlatformShellInterpreterCommandLineArgumentsFor(platform, action[0]) | |
+ else: | |
+ executable = action[0] | |
+ # TODO(nicolas): correct this hardcoded platform quoting | |
+ # NOTE(nicolas): it's tricky. I tried using PlatformQuoteArgument however | |
+ # this would transform the %1 that fasbtuild uses. Maybe we should protect it somehow, | |
+ # quote the arguments, and replace it back to %1 as a last pass. | |
+ quoted_arguments = ['"%s"' % x for x in action[1:]] | |
+ | |
+ executable = shell_which(executable, PlatformGetToolsetPaths(platform, config_name)) | |
PushAssignment(bff, '.ExecExecutable', executable) | |
- # TODO(nicolas): correct this hardcoded platform quoting | |
- # NOTE(nicolas): it's tricky. I tried using PlatformQuoteArgument however | |
- # this would transform the %1 that fasbtuild uses. Maybe we should protect it somehow, | |
- # quote the arguments, and replace it back to %1 as a last pass. | |
- quoted_arguments = ['"%s"' % x for x in action[1:]] | |
PushAssignment( | |
bff, '.ExecArguments', ' '.join(quoted_arguments)) | |
PushAssignment( | |
-- | |
2.7.0.windows.1 | |
From 291d2e58d2d2e4948b6e64db5999dea1d83f3534 Mon Sep 17 00:00:00 2001 | |
From: nil <[email protected]> | |
Date: Tue, 12 Jul 2016 13:19:45 +0200 | |
Subject: [PATCH 06/19] Expand MSVS macros + fix quoting of arguments | |
--- | |
pylib/gyp/generator/fastbuild.py | 43 ++++++++++++++++++++++++++++++++++------ | |
1 file changed, 37 insertions(+), 6 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 4fe1397..2de57ce 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -4,7 +4,9 @@ | |
# This module generates `.bff` files for consumption with the FASTbuild | |
# URL(http://fastbuild.org) opensource build system developed by Franta Fulin. | |
# | |
-# TODO(nicolas): $(IntDir) MSVS macro to be supported. (Or not?) | |
+# TODO(nicolas): Where should PlatformExpandMacros actually be called? | |
+# Every gyp input variable? | |
+# | |
# TODO(nicolas): autopep8, add double spaces after top level functions | |
# | |
# TODO(nicolas): the way the generator creates alias nodes injects | |
@@ -422,6 +424,9 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
def __init__(self): | |
self.msvs_settings = gyp.msvs_emulation.MsvsSettings(target_spec, generator_flags) | |
+ def expand_macros(self, argument, config_name): | |
+ return self.msvs_settings.ConvertVSMacros(argument, config=config_name) | |
+ | |
def get_dll_extension(self): | |
return '.dll' | |
@@ -439,7 +444,8 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
options.append(QuoteShellArgument(argument)) | |
def push_include_dir(self, options, include_dir): | |
- options.append(QuoteShellArgument('/I"%s"' % include_dir)) | |
+ options.append('/I') | |
+ options.append(QuoteShellArgument(include_dir)) | |
def shell_executable(self): | |
return "cmd.exe" | |
@@ -516,7 +522,11 @@ def CompilerOptionsHasCommandLine(compiler_options): | |
def CompilerOptionsConfigure(platform, compiler_options, config_name): | |
# TODO(nicolas): take language into account | |
platform.push_specific_cflags(compiler_options, config_name) | |
- | |
+ new_compiler_options = [ | |
+ PlatformExpandMacros(platform, x, config_name) | |
+ for x in compiler_options | |
+ ] | |
+ compiler_options[:] = new_compiler_options | |
def CompilerOptionsGetCommandLine(compiler_options): | |
return ' '.join(compiler_options) | |
@@ -566,6 +576,17 @@ def PlatformGetDLLExtension(platform): | |
def PlatformQuoteArgument(platform, argument): | |
return platform.quote_argument(argument) | |
+def PlatformExpandMacros(platform, argument, config_name): | |
+ new_argument = platform.expand_macros(argument, config_name) | |
+ # ExpandSpecial in ninja.py | |
+ INTERMEDIATE_DIR = '$!INTERMEDIATE_DIR' | |
+ if INTERMEDIATE_DIR in new_argument: | |
+ new_argument = new_argument.replace(INTERMEDIATE_DIR, generator_default_variables['INTERMEDIATE_DIR']) | |
+ PRODUCT_DIR = '$!PRODUCT_DIR' | |
+ if PRODUCT_DIR in new_argument: | |
+ new_argument = new_argument.replace(PRODUCT_DIR, generator_default_variables['PRODUCT_DIR']) | |
+ return new_argument | |
+ | |
def PlatformGetPathFromGypPath(platform, inputs_base_dir, gyp_path): | |
fragments = gyp_path.split('/') | |
return os.path.abspath( | |
@@ -675,6 +696,11 @@ def PushTargetForConfig( | |
target_name = target_spec['target_name'] | |
def gyp_path_to_os_path(gyp_path): | |
+ gyp_path = PlatformExpandMacros( | |
+ platform, | |
+ gyp_path, | |
+ config_name | |
+ ) | |
return PlatformGetPathFromGypPath(platform, inputs_base_dir, gyp_path) | |
def using_config(): | |
@@ -721,14 +747,17 @@ def PushTargetForConfig( | |
# TODO(nicolas): look in toolset path too | |
if len(action) == 1: | |
executable = PlatformGetShellInterpreter(platform) | |
- quoted_arguments = PlatformShellInterpreterCommandLineArgumentsFor(platform, action[0]) | |
+ quoted_arguments = PlatformShellInterpreterCommandLineArgumentsFor( | |
+ platform, | |
+ PlatformExpandMacros(platform, action[0], config_name) | |
+ ) | |
else: | |
executable = action[0] | |
# TODO(nicolas): correct this hardcoded platform quoting | |
# NOTE(nicolas): it's tricky. I tried using PlatformQuoteArgument however | |
# this would transform the %1 that fasbtuild uses. Maybe we should protect it somehow, | |
# quote the arguments, and replace it back to %1 as a last pass. | |
- quoted_arguments = ['"%s"' % x for x in action[1:]] | |
+ quoted_arguments = ['"%s"' % PlatformExpandMacros(platform, x, config_name) for x in action[1:]] | |
executable = shell_which(executable, PlatformGetToolsetPaths(platform, config_name)) | |
PushAssignment(bff, '.ExecExecutable', executable) | |
@@ -741,7 +770,9 @@ def PushTargetForConfig( | |
action_spec.get( | |
'inputs', | |
[])) | |
- output = gyp_path_to_os_path(action_spec['outputs'][0]) | |
+ output = gyp_path_to_os_path( | |
+ action_spec['outputs'][0], | |
+ ) | |
PushAssignment(bff, '.ExecOutput', output) | |
def compiler_type_for_source(source): | |
-- | |
2.7.0.windows.1 | |
From ebf07a0a36835ee6e70db7a0fb680a02806ebb34 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Wed, 13 Jul 2016 14:15:22 +0200 | |
Subject: [PATCH 07/19] Factorize some ninja code for use in fastbuild | |
generator | |
--- | |
pylib/gyp/generator/common.py | 82 +++++++++++++++++++++++++++++++++++++++++++ | |
pylib/gyp/generator/ninja.py | 76 +++------------------------------------ | |
2 files changed, 86 insertions(+), 72 deletions(-) | |
create mode 100644 pylib/gyp/generator/common.py | |
diff --git a/pylib/gyp/generator/common.py b/pylib/gyp/generator/common.py | |
new file mode 100644 | |
index 0000000..ba908cb | |
--- /dev/null | |
+++ b/pylib/gyp/generator/common.py | |
@@ -0,0 +1,82 @@ | |
+import copy | |
+import gyp | |
+import gyp.common | |
+ | |
+def SetDefaultVariables(default_variables, params): | |
+ global generator_additional_non_configuration_keys | |
+ global generator_additional_path_sections | |
+ flavor = gyp.common.GetFlavor(params) | |
+ if flavor == 'mac': | |
+ default_variables.setdefault('OS', 'mac') | |
+ default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') | |
+ default_variables.setdefault('SHARED_LIB_DIR', | |
+ default_variables['PRODUCT_DIR']) | |
+ default_variables.setdefault('LIB_DIR', | |
+ default_variables['PRODUCT_DIR']) | |
+ elif flavor == 'win': | |
+ exts = gyp.MSVSUtil.TARGET_TYPE_EXT | |
+ default_variables.setdefault('OS', 'win') | |
+ default_variables['EXECUTABLE_SUFFIX'] = '.' + exts['executable'] | |
+ default_variables['STATIC_LIB_PREFIX'] = '' | |
+ default_variables['STATIC_LIB_SUFFIX'] = '.' + exts['static_library'] | |
+ default_variables['SHARED_LIB_PREFIX'] = '' | |
+ default_variables['SHARED_LIB_SUFFIX'] = '.' + exts['shared_library'] | |
+ # Copy additional generator configuration data from VS, which is shared | |
+ # by the Windows Ninja generator. | |
+ import gyp.generator.msvs as msvs_generator | |
+ gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) | |
+ else: | |
+ operating_system = flavor | |
+ if flavor == 'android': | |
+ operating_system = 'linux' # Keep this legacy behavior for now. | |
+ default_variables.setdefault('OS', operating_system) | |
+ default_variables.setdefault('SHARED_LIB_SUFFIX', '.so') | |
+ default_variables.setdefault('SHARED_LIB_DIR', | |
+ os.path.join(default_variables['PRODUCT_DIR'], 'lib')) | |
+ default_variables.setdefault('LIB_DIR', | |
+ os.path.join(default_variables['PRODUCT_DIR'], 'obj')) | |
+ | |
+def ComputeOutputFileName(spec, type, default_variables): | |
+ """Compute the filename of the final output for the current target.""" | |
+ | |
+ # Compute filename prefix: the product prefix, or a default for | |
+ # the product type. | |
+ DEFAULT_PREFIX = { | |
+ 'loadable_module': default_variables['SHARED_LIB_PREFIX'], | |
+ 'shared_library': default_variables['SHARED_LIB_PREFIX'], | |
+ 'static_library': default_variables['STATIC_LIB_PREFIX'], | |
+ 'executable': default_variables['EXECUTABLE_PREFIX'], | |
+ } | |
+ prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, '')) | |
+ | |
+ # Compute filename extension: the product extension, or a default | |
+ # for the product type. | |
+ DEFAULT_EXTENSION = { | |
+ 'loadable_module': default_variables['SHARED_LIB_SUFFIX'], | |
+ 'shared_library': default_variables['SHARED_LIB_SUFFIX'], | |
+ 'static_library': default_variables['STATIC_LIB_SUFFIX'], | |
+ 'executable': default_variables['EXECUTABLE_SUFFIX'], | |
+ } | |
+ extension = spec.get('product_extension') | |
+ if extension: | |
+ extension = '.' + extension | |
+ else: | |
+ extension = DEFAULT_EXTENSION.get(type, '') | |
+ | |
+ if 'product_name' in spec: | |
+ # If we were given an explicit name, use that. | |
+ target = spec['product_name'] | |
+ else: | |
+ # Otherwise, derive a name from the target name. | |
+ target = spec['target_name'] | |
+ if prefix == 'lib': | |
+ # Snip out an extra 'lib' from libs if appropriate. | |
+ target = StripPrefix(target, 'lib') | |
+ | |
+ if type in ('static_library', 'loadable_module', 'shared_library', | |
+ 'executable'): | |
+ return '%s%s%s' % (prefix, target, extension) | |
+ elif type == 'none': | |
+ return '%s.stamp' % target | |
+ else: | |
+ raise Exception('Unhandled output type %s' % type) | |
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py | |
index 9cfc706..de213e1 100644 | |
--- a/pylib/gyp/generator/ninja.py | |
+++ b/pylib/gyp/generator/ninja.py | |
@@ -14,6 +14,7 @@ import subprocess | |
import sys | |
import gyp | |
import gyp.common | |
+import gyp.generator.common as gcommon | |
from gyp.common import OrderedSet | |
import gyp.msvs_emulation | |
import gyp.MSVSUtil as MSVSUtil | |
@@ -1475,54 +1476,11 @@ class NinjaWriter(object): | |
os.path.join(path, self.xcode_settings.GetWrapperName())) | |
def ComputeOutputFileName(self, spec, type=None): | |
- """Compute the filename of the final output for the current target.""" | |
if not type: | |
- type = spec['type'] | |
- | |
+ type = spec['type'] | |
default_variables = copy.copy(generator_default_variables) | |
CalculateVariables(default_variables, {'flavor': self.flavor}) | |
- | |
- # Compute filename prefix: the product prefix, or a default for | |
- # the product type. | |
- DEFAULT_PREFIX = { | |
- 'loadable_module': default_variables['SHARED_LIB_PREFIX'], | |
- 'shared_library': default_variables['SHARED_LIB_PREFIX'], | |
- 'static_library': default_variables['STATIC_LIB_PREFIX'], | |
- 'executable': default_variables['EXECUTABLE_PREFIX'], | |
- } | |
- prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, '')) | |
- | |
- # Compute filename extension: the product extension, or a default | |
- # for the product type. | |
- DEFAULT_EXTENSION = { | |
- 'loadable_module': default_variables['SHARED_LIB_SUFFIX'], | |
- 'shared_library': default_variables['SHARED_LIB_SUFFIX'], | |
- 'static_library': default_variables['STATIC_LIB_SUFFIX'], | |
- 'executable': default_variables['EXECUTABLE_SUFFIX'], | |
- } | |
- extension = spec.get('product_extension') | |
- if extension: | |
- extension = '.' + extension | |
- else: | |
- extension = DEFAULT_EXTENSION.get(type, '') | |
- | |
- if 'product_name' in spec: | |
- # If we were given an explicit name, use that. | |
- target = spec['product_name'] | |
- else: | |
- # Otherwise, derive a name from the target name. | |
- target = spec['target_name'] | |
- if prefix == 'lib': | |
- # Snip out an extra 'lib' from libs if appropriate. | |
- target = StripPrefix(target, 'lib') | |
- | |
- if type in ('static_library', 'loadable_module', 'shared_library', | |
- 'executable'): | |
- return '%s%s%s' % (prefix, target, extension) | |
- elif type == 'none': | |
- return '%s.stamp' % target | |
- else: | |
- raise Exception('Unhandled output type %s' % type) | |
+ return gcommon.ComputeOutputFileName(spec, type, default_variables) | |
def ComputeOutput(self, spec, arch=None): | |
"""Compute the path for the final output of the spec.""" | |
@@ -1648,13 +1606,6 @@ def CalculateVariables(default_variables, params): | |
global generator_additional_path_sections | |
flavor = gyp.common.GetFlavor(params) | |
if flavor == 'mac': | |
- default_variables.setdefault('OS', 'mac') | |
- default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') | |
- default_variables.setdefault('SHARED_LIB_DIR', | |
- generator_default_variables['PRODUCT_DIR']) | |
- default_variables.setdefault('LIB_DIR', | |
- generator_default_variables['PRODUCT_DIR']) | |
- | |
# Copy additional generator configuration data from Xcode, which is shared | |
# by the Mac Ninja generator. | |
import gyp.generator.xcode as xcode_generator | |
@@ -1666,14 +1617,6 @@ def CalculateVariables(default_variables, params): | |
generator_extra_sources_for_rules = getattr(xcode_generator, | |
'generator_extra_sources_for_rules', []) | |
elif flavor == 'win': | |
- exts = gyp.MSVSUtil.TARGET_TYPE_EXT | |
- default_variables.setdefault('OS', 'win') | |
- default_variables['EXECUTABLE_SUFFIX'] = '.' + exts['executable'] | |
- default_variables['STATIC_LIB_PREFIX'] = '' | |
- default_variables['STATIC_LIB_SUFFIX'] = '.' + exts['static_library'] | |
- default_variables['SHARED_LIB_PREFIX'] = '' | |
- default_variables['SHARED_LIB_SUFFIX'] = '.' + exts['shared_library'] | |
- | |
# Copy additional generator configuration data from VS, which is shared | |
# by the Windows Ninja generator. | |
import gyp.generator.msvs as msvs_generator | |
@@ -1681,18 +1624,7 @@ def CalculateVariables(default_variables, params): | |
'generator_additional_non_configuration_keys', []) | |
generator_additional_path_sections = getattr(msvs_generator, | |
'generator_additional_path_sections', []) | |
- | |
- gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) | |
- else: | |
- operating_system = flavor | |
- if flavor == 'android': | |
- operating_system = 'linux' # Keep this legacy behavior for now. | |
- default_variables.setdefault('OS', operating_system) | |
- default_variables.setdefault('SHARED_LIB_SUFFIX', '.so') | |
- default_variables.setdefault('SHARED_LIB_DIR', | |
- os.path.join('$!PRODUCT_DIR', 'lib')) | |
- default_variables.setdefault('LIB_DIR', | |
- os.path.join('$!PRODUCT_DIR', 'obj')) | |
+ gcommon.SetDefaultVariables(default_variables, params); | |
def ComputeOutputDir(params): | |
"""Returns the path from the toplevel_dir to the build output directory.""" | |
-- | |
2.7.0.windows.1 | |
From 01eef231489203c8fa40a011e3b0a4eca0a6488b Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Wed, 13 Jul 2016 14:15:38 +0200 | |
Subject: [PATCH 08/19] Handle empty target list | |
--- | |
pylib/gyp/generator/fastbuild.py | 2 ++ | |
1 file changed, 2 insertions(+) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 2de57ce..dd99bb8 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -145,6 +145,8 @@ def GenerateOutput(target_list, target_dicts, data, params): | |
return bff_basename | |
def get_config_names(target_list, target_dicts): | |
+ if not target_list: | |
+ return [] | |
first_target = target_list[0] | |
return target_dicts[first_target]['configurations'].keys() | |
-- | |
2.7.0.windows.1 | |
From 314d1d17bdd58f4e8eaea42b217218e338321b2c Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Wed, 13 Jul 2016 14:15:50 +0200 | |
Subject: [PATCH 09/19] Pick an arbitrary config name for now | |
--- | |
pylib/gyp/generator/fastbuild.py | 2 +- | |
1 file changed, 1 insertion(+), 1 deletion(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index dd99bb8..fa2cd31 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -166,7 +166,7 @@ For flavor: %s | |
# TODO(nicolas): remove, this is needed for now because I don't know how to | |
# avoid duplicating actions which lead to the same output across | |
# configs | |
- config_names = [y for y in config_names if y == 'Debug'] | |
+ config_names = config_names[0:1] if config_names else [] | |
generator_flags = params.get('generator_flags', {}) | |
toolset = MakeToolset(flavor, generator_flags) | |
-- | |
2.7.0.windows.1 | |
From 554934112743a17ec247724fc4658c77b9f337c5 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Wed, 13 Jul 2016 14:16:21 +0200 | |
Subject: [PATCH 10/19] Compute accurate product filename | |
We reuse the ninja logic | |
--- | |
pylib/gyp/generator/fastbuild.py | 29 ++++++++++++++++++----------- | |
1 file changed, 18 insertions(+), 11 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index fa2cd31..17bd531 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -28,6 +28,7 @@ import gyp.msvs_emulation | |
import gyp.xcode_emulation | |
import os | |
import re | |
+import gyp.generator.common as gcommon | |
from collections import defaultdict | |
from itertools import product | |
@@ -109,6 +110,7 @@ def CalculateVariables(default_variables, params): | |
# fastbuild supports windows, mac and linux | |
flavor = gyp.common.GetFlavor(params) | |
default_variables.setdefault('OS', flavor) | |
+ gcommon.SetDefaultVariables(default_variables, {'flavor': flavor}) | |
# TODO(nicolas): clean this list up | |
generator_default_variables = { | |
@@ -192,8 +194,12 @@ For flavor: %s | |
) | |
inputs_base_dir = os.path.dirname(target_build_file) | |
platform = MakePlatform(flavor, generator_flags, toolset, target_spec) | |
+ default_variables = copy.copy(generator_default_variables) | |
+ gcommon.SetDefaultVariables(default_variables, {'flavor': flavor}) | |
+ target_output_filename = gcommon.ComputeOutputFileName(target_spec, target_spec['type'], default_variables) | |
PushTargetSpecs( | |
bff, | |
+ target_output_filename, | |
platform, | |
inputs_base_dir, | |
qualified_target, | |
@@ -461,7 +467,7 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
def linker_options_for_dll(self): | |
return '/DLL' | |
- def push_specific_ldflags(self, options, inputs_base_dir, config_name, target_type, target_product_name): | |
+ def push_specific_ldflags(self, options, inputs_base_dir, config_name, target_type, target_output_filename): | |
# TODO(nicolas): remove those hardcoded sentinels and replace with correct | |
# stuff (I need to figure out what there are for) | |
def gyp_to_build_path(x): | |
@@ -469,7 +475,7 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
def expand_special(path, product_dir=None): | |
return "EXPAND_SPECIAL(%s, %s)" % (path, product_dir) | |
manifest_base_name = "MANIFEST_BASE_NAME" | |
- output_name = "$ProductDir$/%s" % target_product_name | |
+ output_name = "$ProductDir$/%s" % target_output_filename | |
is_executable = target_type == 'executable' | |
build_dir = "BUILD_DIR" | |
@@ -537,14 +543,14 @@ def CompilerOptionsGetCommandLine(compiler_options): | |
def MakeLinkerOptions(platform): | |
return [] | |
-def LinkerOptionsConfigure(platform, linker_options, inputs_base_dir, config_name, target_type, target_product_name): | |
+def LinkerOptionsConfigure(platform, linker_options, inputs_base_dir, config_name, target_type, target_output_filename): | |
if target_type != 'executable': | |
# NOTE(nil): TAG(platform) FASTbuild figures out the type of a | |
# node by the presence of certain linker flags. Therefore the | |
# right flags are necessary. Forgetting it shows up as a | |
# FASTbuild complaint during dependency checking. | |
linker_options.append(platform.linker_options_for_dll()) | |
- platform.push_specific_ldflags(linker_options, inputs_base_dir, config_name, target_type, target_product_name) | |
+ platform.push_specific_ldflags(linker_options, inputs_base_dir, config_name, target_type, target_output_filename) | |
def LinkerOptionsHasCommandLine(linker_options): | |
@@ -605,6 +611,7 @@ def PlatformGetPathFromGypPath(platform, inputs_base_dir, gyp_path): | |
def PushTargetSpecs( | |
bff, | |
+ output_filename, | |
platform, | |
inputs_base_dir, | |
qualified_target, | |
@@ -663,6 +670,7 @@ def PushTargetSpecs( | |
if PushTargetForConfig( | |
bff, | |
+ output_filename, | |
platform, | |
inputs_base_dir, | |
target_spec, | |
@@ -687,6 +695,7 @@ def PushTargetSpecs( | |
def PushTargetForConfig( | |
bff, | |
+ output_filename, | |
platform, | |
inputs_base_dir, | |
target_spec, | |
@@ -990,8 +999,8 @@ def PushTargetForConfig( | |
shared_library=PlatformGetDLLExtension(platform), | |
loadable_module=PlatformGetDLLExtension(platform), | |
)[target_type] | |
- linker_product = "%s%s" % ( | |
- target_spec['product_name'], linker_product_suffix) | |
+ assert output_filename.endswith(linker_product_suffix) | |
+ linker_product = output_filename | |
linker_function = dict( | |
executable='Executable', | |
shared_library='DLL', | |
@@ -1010,7 +1019,7 @@ def PushTargetForConfig( | |
inputs_base_dir, | |
config_name, | |
target_type, | |
- target_spec['product_name'] | |
+ linker_product | |
) | |
if LinkerOptionsHasCommandLine(linker_options): | |
PushCommand( | |
@@ -1053,13 +1062,11 @@ def PushTargetForConfig( | |
with BFFBlock(bff, BFFFunctionBegin('Library', [library_name])): | |
using_config() | |
using_compiler(target_toolset, config_name, CompilerType.CXX) | |
- | |
- # TODO(nil): change, this shouldnt be hardcoded | |
PushAssignment( | |
bff, | |
'.LibrarianOutput', | |
- '$ProductDir$/Lib%s.lib' % | |
- target_spec['product_name']) | |
+ '$ProductDir$/%s' % output_filename | |
+ ) | |
PushAssignment(bff, | |
'.LibrarianAdditionalInputs', | |
bff_all_object_list_names) | |
-- | |
2.7.0.windows.1 | |
From 069592f6d475d8f213d8b74f5c8584c6e65b60cf Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Thu, 14 Jul 2016 09:13:15 +0200 | |
Subject: [PATCH 11/19] Find clearer name | |
--- | |
pylib/gyp/generator/common.py | 4 +--- | |
pylib/gyp/generator/fastbuild.py | 4 ++-- | |
pylib/gyp/generator/ninja.py | 2 +- | |
3 files changed, 4 insertions(+), 6 deletions(-) | |
diff --git a/pylib/gyp/generator/common.py b/pylib/gyp/generator/common.py | |
index ba908cb..92aa8b9 100644 | |
--- a/pylib/gyp/generator/common.py | |
+++ b/pylib/gyp/generator/common.py | |
@@ -2,9 +2,7 @@ import copy | |
import gyp | |
import gyp.common | |
-def SetDefaultVariables(default_variables, params): | |
- global generator_additional_non_configuration_keys | |
- global generator_additional_path_sections | |
+def SetPlatformDefaultVariables(default_variables, params): | |
flavor = gyp.common.GetFlavor(params) | |
if flavor == 'mac': | |
default_variables.setdefault('OS', 'mac') | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 17bd531..793424b 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -110,7 +110,7 @@ def CalculateVariables(default_variables, params): | |
# fastbuild supports windows, mac and linux | |
flavor = gyp.common.GetFlavor(params) | |
default_variables.setdefault('OS', flavor) | |
- gcommon.SetDefaultVariables(default_variables, {'flavor': flavor}) | |
+ gcommon.SetPlatformDefaultVariables(default_variables, {'flavor': flavor}) | |
# TODO(nicolas): clean this list up | |
generator_default_variables = { | |
@@ -195,7 +195,7 @@ For flavor: %s | |
inputs_base_dir = os.path.dirname(target_build_file) | |
platform = MakePlatform(flavor, generator_flags, toolset, target_spec) | |
default_variables = copy.copy(generator_default_variables) | |
- gcommon.SetDefaultVariables(default_variables, {'flavor': flavor}) | |
+ gcommon.SetPlatformDefaultVariables(default_variables, {'flavor': flavor}) | |
target_output_filename = gcommon.ComputeOutputFileName(target_spec, target_spec['type'], default_variables) | |
PushTargetSpecs( | |
bff, | |
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py | |
index de213e1..425b238 100644 | |
--- a/pylib/gyp/generator/ninja.py | |
+++ b/pylib/gyp/generator/ninja.py | |
@@ -1624,7 +1624,7 @@ def CalculateVariables(default_variables, params): | |
'generator_additional_non_configuration_keys', []) | |
generator_additional_path_sections = getattr(msvs_generator, | |
'generator_additional_path_sections', []) | |
- gcommon.SetDefaultVariables(default_variables, params); | |
+ gcommon.SetPlatformDefaultVariables(default_variables, params); | |
def ComputeOutputDir(params): | |
"""Returns the path from the toplevel_dir to the build output directory.""" | |
-- | |
2.7.0.windows.1 | |
From e47375e454fead3b9392b939baf4971f1ea6fc70 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Thu, 14 Jul 2016 09:17:50 +0200 | |
Subject: [PATCH 12/19] Move utils to generator_utils | |
Keep library code away from the generator list | |
--- | |
pylib/gyp/generator/common.py | 80 ---------------------------------------- | |
pylib/gyp/generator/fastbuild.py | 8 ++-- | |
pylib/gyp/generator/ninja.py | 6 +-- | |
pylib/gyp/generator_utils.py | 80 ++++++++++++++++++++++++++++++++++++++++ | |
4 files changed, 87 insertions(+), 87 deletions(-) | |
delete mode 100644 pylib/gyp/generator/common.py | |
create mode 100644 pylib/gyp/generator_utils.py | |
diff --git a/pylib/gyp/generator/common.py b/pylib/gyp/generator/common.py | |
deleted file mode 100644 | |
index 92aa8b9..0000000 | |
--- a/pylib/gyp/generator/common.py | |
+++ /dev/null | |
@@ -1,80 +0,0 @@ | |
-import copy | |
-import gyp | |
-import gyp.common | |
- | |
-def SetPlatformDefaultVariables(default_variables, params): | |
- flavor = gyp.common.GetFlavor(params) | |
- if flavor == 'mac': | |
- default_variables.setdefault('OS', 'mac') | |
- default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') | |
- default_variables.setdefault('SHARED_LIB_DIR', | |
- default_variables['PRODUCT_DIR']) | |
- default_variables.setdefault('LIB_DIR', | |
- default_variables['PRODUCT_DIR']) | |
- elif flavor == 'win': | |
- exts = gyp.MSVSUtil.TARGET_TYPE_EXT | |
- default_variables.setdefault('OS', 'win') | |
- default_variables['EXECUTABLE_SUFFIX'] = '.' + exts['executable'] | |
- default_variables['STATIC_LIB_PREFIX'] = '' | |
- default_variables['STATIC_LIB_SUFFIX'] = '.' + exts['static_library'] | |
- default_variables['SHARED_LIB_PREFIX'] = '' | |
- default_variables['SHARED_LIB_SUFFIX'] = '.' + exts['shared_library'] | |
- # Copy additional generator configuration data from VS, which is shared | |
- # by the Windows Ninja generator. | |
- import gyp.generator.msvs as msvs_generator | |
- gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) | |
- else: | |
- operating_system = flavor | |
- if flavor == 'android': | |
- operating_system = 'linux' # Keep this legacy behavior for now. | |
- default_variables.setdefault('OS', operating_system) | |
- default_variables.setdefault('SHARED_LIB_SUFFIX', '.so') | |
- default_variables.setdefault('SHARED_LIB_DIR', | |
- os.path.join(default_variables['PRODUCT_DIR'], 'lib')) | |
- default_variables.setdefault('LIB_DIR', | |
- os.path.join(default_variables['PRODUCT_DIR'], 'obj')) | |
- | |
-def ComputeOutputFileName(spec, type, default_variables): | |
- """Compute the filename of the final output for the current target.""" | |
- | |
- # Compute filename prefix: the product prefix, or a default for | |
- # the product type. | |
- DEFAULT_PREFIX = { | |
- 'loadable_module': default_variables['SHARED_LIB_PREFIX'], | |
- 'shared_library': default_variables['SHARED_LIB_PREFIX'], | |
- 'static_library': default_variables['STATIC_LIB_PREFIX'], | |
- 'executable': default_variables['EXECUTABLE_PREFIX'], | |
- } | |
- prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, '')) | |
- | |
- # Compute filename extension: the product extension, or a default | |
- # for the product type. | |
- DEFAULT_EXTENSION = { | |
- 'loadable_module': default_variables['SHARED_LIB_SUFFIX'], | |
- 'shared_library': default_variables['SHARED_LIB_SUFFIX'], | |
- 'static_library': default_variables['STATIC_LIB_SUFFIX'], | |
- 'executable': default_variables['EXECUTABLE_SUFFIX'], | |
- } | |
- extension = spec.get('product_extension') | |
- if extension: | |
- extension = '.' + extension | |
- else: | |
- extension = DEFAULT_EXTENSION.get(type, '') | |
- | |
- if 'product_name' in spec: | |
- # If we were given an explicit name, use that. | |
- target = spec['product_name'] | |
- else: | |
- # Otherwise, derive a name from the target name. | |
- target = spec['target_name'] | |
- if prefix == 'lib': | |
- # Snip out an extra 'lib' from libs if appropriate. | |
- target = StripPrefix(target, 'lib') | |
- | |
- if type in ('static_library', 'loadable_module', 'shared_library', | |
- 'executable'): | |
- return '%s%s%s' % (prefix, target, extension) | |
- elif type == 'none': | |
- return '%s.stamp' % target | |
- else: | |
- raise Exception('Unhandled output type %s' % type) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 793424b..a6b2ffb 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -28,7 +28,7 @@ import gyp.msvs_emulation | |
import gyp.xcode_emulation | |
import os | |
import re | |
-import gyp.generator.common as gcommon | |
+import gyp.generator_utils as gutils | |
from collections import defaultdict | |
from itertools import product | |
@@ -110,7 +110,7 @@ def CalculateVariables(default_variables, params): | |
# fastbuild supports windows, mac and linux | |
flavor = gyp.common.GetFlavor(params) | |
default_variables.setdefault('OS', flavor) | |
- gcommon.SetPlatformDefaultVariables(default_variables, {'flavor': flavor}) | |
+ gutils.SetPlatformDefaultVariables(default_variables, {'flavor': flavor}) | |
# TODO(nicolas): clean this list up | |
generator_default_variables = { | |
@@ -195,8 +195,8 @@ For flavor: %s | |
inputs_base_dir = os.path.dirname(target_build_file) | |
platform = MakePlatform(flavor, generator_flags, toolset, target_spec) | |
default_variables = copy.copy(generator_default_variables) | |
- gcommon.SetPlatformDefaultVariables(default_variables, {'flavor': flavor}) | |
- target_output_filename = gcommon.ComputeOutputFileName(target_spec, target_spec['type'], default_variables) | |
+ gutils.SetPlatformDefaultVariables(default_variables, {'flavor': flavor}) | |
+ target_output_filename = gutils.ComputeOutputFileName(target_spec, target_spec['type'], default_variables) | |
PushTargetSpecs( | |
bff, | |
target_output_filename, | |
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py | |
index 425b238..15a75ba 100644 | |
--- a/pylib/gyp/generator/ninja.py | |
+++ b/pylib/gyp/generator/ninja.py | |
@@ -14,7 +14,7 @@ import subprocess | |
import sys | |
import gyp | |
import gyp.common | |
-import gyp.generator.common as gcommon | |
+import gyp.generator_utils as gutils | |
from gyp.common import OrderedSet | |
import gyp.msvs_emulation | |
import gyp.MSVSUtil as MSVSUtil | |
@@ -1480,7 +1480,7 @@ class NinjaWriter(object): | |
type = spec['type'] | |
default_variables = copy.copy(generator_default_variables) | |
CalculateVariables(default_variables, {'flavor': self.flavor}) | |
- return gcommon.ComputeOutputFileName(spec, type, default_variables) | |
+ return gutils.ComputeOutputFileName(spec, type, default_variables) | |
def ComputeOutput(self, spec, arch=None): | |
"""Compute the path for the final output of the spec.""" | |
@@ -1624,7 +1624,7 @@ def CalculateVariables(default_variables, params): | |
'generator_additional_non_configuration_keys', []) | |
generator_additional_path_sections = getattr(msvs_generator, | |
'generator_additional_path_sections', []) | |
- gcommon.SetPlatformDefaultVariables(default_variables, params); | |
+ gutils.SetPlatformDefaultVariables(default_variables, params); | |
def ComputeOutputDir(params): | |
"""Returns the path from the toplevel_dir to the build output directory.""" | |
diff --git a/pylib/gyp/generator_utils.py b/pylib/gyp/generator_utils.py | |
new file mode 100644 | |
index 0000000..92aa8b9 | |
--- /dev/null | |
+++ b/pylib/gyp/generator_utils.py | |
@@ -0,0 +1,80 @@ | |
+import copy | |
+import gyp | |
+import gyp.common | |
+ | |
+def SetPlatformDefaultVariables(default_variables, params): | |
+ flavor = gyp.common.GetFlavor(params) | |
+ if flavor == 'mac': | |
+ default_variables.setdefault('OS', 'mac') | |
+ default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') | |
+ default_variables.setdefault('SHARED_LIB_DIR', | |
+ default_variables['PRODUCT_DIR']) | |
+ default_variables.setdefault('LIB_DIR', | |
+ default_variables['PRODUCT_DIR']) | |
+ elif flavor == 'win': | |
+ exts = gyp.MSVSUtil.TARGET_TYPE_EXT | |
+ default_variables.setdefault('OS', 'win') | |
+ default_variables['EXECUTABLE_SUFFIX'] = '.' + exts['executable'] | |
+ default_variables['STATIC_LIB_PREFIX'] = '' | |
+ default_variables['STATIC_LIB_SUFFIX'] = '.' + exts['static_library'] | |
+ default_variables['SHARED_LIB_PREFIX'] = '' | |
+ default_variables['SHARED_LIB_SUFFIX'] = '.' + exts['shared_library'] | |
+ # Copy additional generator configuration data from VS, which is shared | |
+ # by the Windows Ninja generator. | |
+ import gyp.generator.msvs as msvs_generator | |
+ gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) | |
+ else: | |
+ operating_system = flavor | |
+ if flavor == 'android': | |
+ operating_system = 'linux' # Keep this legacy behavior for now. | |
+ default_variables.setdefault('OS', operating_system) | |
+ default_variables.setdefault('SHARED_LIB_SUFFIX', '.so') | |
+ default_variables.setdefault('SHARED_LIB_DIR', | |
+ os.path.join(default_variables['PRODUCT_DIR'], 'lib')) | |
+ default_variables.setdefault('LIB_DIR', | |
+ os.path.join(default_variables['PRODUCT_DIR'], 'obj')) | |
+ | |
+def ComputeOutputFileName(spec, type, default_variables): | |
+ """Compute the filename of the final output for the current target.""" | |
+ | |
+ # Compute filename prefix: the product prefix, or a default for | |
+ # the product type. | |
+ DEFAULT_PREFIX = { | |
+ 'loadable_module': default_variables['SHARED_LIB_PREFIX'], | |
+ 'shared_library': default_variables['SHARED_LIB_PREFIX'], | |
+ 'static_library': default_variables['STATIC_LIB_PREFIX'], | |
+ 'executable': default_variables['EXECUTABLE_PREFIX'], | |
+ } | |
+ prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, '')) | |
+ | |
+ # Compute filename extension: the product extension, or a default | |
+ # for the product type. | |
+ DEFAULT_EXTENSION = { | |
+ 'loadable_module': default_variables['SHARED_LIB_SUFFIX'], | |
+ 'shared_library': default_variables['SHARED_LIB_SUFFIX'], | |
+ 'static_library': default_variables['STATIC_LIB_SUFFIX'], | |
+ 'executable': default_variables['EXECUTABLE_SUFFIX'], | |
+ } | |
+ extension = spec.get('product_extension') | |
+ if extension: | |
+ extension = '.' + extension | |
+ else: | |
+ extension = DEFAULT_EXTENSION.get(type, '') | |
+ | |
+ if 'product_name' in spec: | |
+ # If we were given an explicit name, use that. | |
+ target = spec['product_name'] | |
+ else: | |
+ # Otherwise, derive a name from the target name. | |
+ target = spec['target_name'] | |
+ if prefix == 'lib': | |
+ # Snip out an extra 'lib' from libs if appropriate. | |
+ target = StripPrefix(target, 'lib') | |
+ | |
+ if type in ('static_library', 'loadable_module', 'shared_library', | |
+ 'executable'): | |
+ return '%s%s%s' % (prefix, target, extension) | |
+ elif type == 'none': | |
+ return '%s.stamp' % target | |
+ else: | |
+ raise Exception('Unhandled output type %s' % type) | |
-- | |
2.7.0.windows.1 | |
From 4b76adf9c3d40a97f02f4aa4a4a04cf217749b18 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Thu, 14 Jul 2016 10:04:09 +0200 | |
Subject: [PATCH 13/19] Pass output directory for manifest generation | |
--- | |
pylib/gyp/generator/fastbuild.py | 20 +++++++++++--------- | |
1 file changed, 11 insertions(+), 9 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index a6b2ffb..793ed5d 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -174,12 +174,12 @@ For flavor: %s | |
toolset = MakeToolset(flavor, generator_flags) | |
for qualified_target in target_list: | |
- target_build_file, target_name, target_toolset = \ | |
+ target_build_file, target_name, target_toolset_name = \ | |
gyp.common.ParseQualifiedTarget(qualified_target) | |
- if target_toolset not in toolsets_with_bff_config: | |
- PushToolsetConfig(bff, target_toolset, config_names) | |
- toolsets_with_bff_config.add(target_toolset) | |
+ if target_toolset_name not in toolsets_with_bff_config: | |
+ PushToolsetConfig(bff, target_toolset_name, config_names) | |
+ toolsets_with_bff_config.add(target_toolset_name) | |
target_spec = target_dicts[qualified_target] | |
PushHeadingComment(bff, 'target: %s (type: %s)' % | |
@@ -193,7 +193,9 @@ For flavor: %s | |
target_spec | |
) | |
inputs_base_dir = os.path.dirname(target_build_file) | |
- platform = MakePlatform(flavor, generator_flags, toolset, target_spec) | |
+ platform = MakePlatform( | |
+ flavor, generator_flags, toolset, params['options'].toplevel_dir, target_spec | |
+ ) | |
default_variables = copy.copy(generator_default_variables) | |
gutils.SetPlatformDefaultVariables(default_variables, {'flavor': flavor}) | |
target_output_filename = gutils.ComputeOutputFileName(target_spec, target_spec['type'], default_variables) | |
@@ -205,7 +207,7 @@ For flavor: %s | |
qualified_target, | |
target_spec, | |
target_dicts, | |
- config_names, | |
+ config_names | |
) | |
BFFFinalize(bff) | |
BFFWriteToFile(bff, bff_path) | |
@@ -371,7 +373,8 @@ def MakeToolset(flavor, generator_flags): | |
return toolset | |
-def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
+def MakePlatform(flavor, generator_flags, toolset, toplevel_dir, target_spec): | |
+ outputs_base_dir = os.path.join('output', target_spec['toolset']) | |
if flavor == 'mac': | |
def QuoteShellArgument(arg): | |
# NOTE(nil): adapted from ninja.py | |
@@ -477,7 +480,6 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
manifest_base_name = "MANIFEST_BASE_NAME" | |
output_name = "$ProductDir$/%s" % target_output_filename | |
is_executable = target_type == 'executable' | |
- build_dir = "BUILD_DIR" | |
ldflags,\ | |
intermediate_manifest,\ | |
@@ -488,7 +490,7 @@ def MakePlatform(flavor, generator_flags, toolset, target_spec): | |
manifest_base_name, | |
output_name, | |
is_executable, | |
- build_dir | |
+ outputs_base_dir | |
) | |
options.extend(ldflags) | |
-- | |
2.7.0.windows.1 | |
From ca276fa5429f3b800c9f82ca34dbad583b0dc572 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Fri, 15 Jul 2016 09:35:27 +0200 | |
Subject: [PATCH 14/19] Quote all tools command line options (fixes PDB w/ | |
spaces) | |
--- | |
pylib/gyp/generator/fastbuild.py | 24 ++++++++++++++---------- | |
1 file changed, 14 insertions(+), 10 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 793ed5d..d251326 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -452,11 +452,11 @@ def MakePlatform(flavor, generator_flags, toolset, toplevel_dir, target_spec): | |
argument = '/D%s' % flag | |
# NOTE(nil): cl.exe interprets # as = | |
argument.replace('#', '\\%03o' % ord('#')) | |
- options.append(QuoteShellArgument(argument)) | |
+ options.append(argument) | |
def push_include_dir(self, options, include_dir): | |
options.append('/I') | |
- options.append(QuoteShellArgument(include_dir)) | |
+ options.append(include_dir) | |
def shell_executable(self): | |
return "cmd.exe" | |
@@ -500,7 +500,7 @@ def MakePlatform(flavor, generator_flags, toolset, toplevel_dir, target_spec): | |
def ldflags_for_library_dirs(self, library_dirs): | |
flags = [] | |
for library_dir in library_dirs: | |
- flags += ['/LIBPATH:"%s"' % library_dir] | |
+ flags += ['/LIBPATH:%s' % library_dir] | |
return flags | |
return WinPlatform() | |
@@ -538,8 +538,12 @@ def CompilerOptionsConfigure(platform, compiler_options, config_name): | |
] | |
compiler_options[:] = new_compiler_options | |
-def CompilerOptionsGetCommandLine(compiler_options): | |
- return ' '.join(compiler_options) | |
+def PlatformGetCommandLine(platform, options): | |
+ return ' '.join([PlatformQuoteArgument(platform, x) for x in options]) | |
+ | |
+ | |
+def CompilerOptionsGetCommandLine(platform, compiler_options): | |
+ return PlatformGetCommandLine(platform, compiler_options) | |
def MakeLinkerOptions(platform): | |
@@ -559,8 +563,8 @@ def LinkerOptionsHasCommandLine(linker_options): | |
return len(linker_options) > 0 | |
-def LinkerOptionsGetCommandLine(linker_options): | |
- return ' '.join(linker_options) | |
+def LinkerOptionsGetCommandLine(platform, linker_options): | |
+ return PlatformGetCommandLine(platform, linker_options) | |
# TODO(nicolas): what about moving that to linker options function | |
@@ -915,7 +919,7 @@ def PushTargetForConfig( | |
bff, | |
BFFConcatenation( | |
'.CompilerOptions', | |
- ' ' + CompilerOptionsGetCommandLine(compiler_options), | |
+ ' ' + CompilerOptionsGetCommandLine(platform, compiler_options), | |
) | |
) | |
@@ -943,7 +947,7 @@ def PushTargetForConfig( | |
bff, | |
BFFConcatenation( | |
'.CompilerOptions', | |
- ' ' + CompilerOptionsGetCommandLine(compiler_options) | |
+ ' ' + CompilerOptionsGetCommandLine(platform, compiler_options) | |
) | |
) | |
PushAssignment( | |
@@ -1028,7 +1032,7 @@ def PushTargetForConfig( | |
bff, | |
BFFConcatenation( | |
'.LinkerOptions', | |
- ' ' + LinkerOptionsGetCommandLine(linker_options) | |
+ ' ' + LinkerOptionsGetCommandLine(platform, linker_options) | |
) | |
) | |
-- | |
2.7.0.windows.1 | |
From a1ee5549cb4511bc4c38412da019b65624299d85 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Fri, 15 Jul 2016 09:35:49 +0200 | |
Subject: [PATCH 15/19] Format all paths in the native platform syntax | |
--- | |
pylib/gyp/generator/fastbuild.py | 17 ++++++++--------- | |
1 file changed, 8 insertions(+), 9 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index d251326..ecfbecc 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -225,7 +225,7 @@ def PushToolsetConfig(bff, toolset, config_names): | |
BFFAssignment('.SharedProductDir', '%s' % products_base_dir), | |
BFFAssignment( | |
'.SharedIntermediateDir', | |
- '$SharedProductDir$/gen' | |
+ os.path.join('$SharedProductDir$', 'gen') | |
), | |
# Are these ProductDir/IntermediateDir legal when outside of any | |
# configuration? TODO(nicolas): or change gyp, or change rules to | |
@@ -257,9 +257,8 @@ def PushConfig(bff, bff_config_name, config_name, toolset): | |
[ | |
BFFUsing(GetBFFConfigVariableName(toolset, '')), | |
BFFAssignment('.ConfigName', '%s' % config_name), | |
- BFFAssignment('.ProductDir', '$SharedProductDir$/%s' % | |
- config_name), | |
- BFFAssignment('.IntermediateDir', '$ProductDir$/gen'), | |
+ BFFAssignment('.ProductDir', os.path.join('$SharedProductDir$', config_name)), | |
+ BFFAssignment('.IntermediateDir', os.path.join('$ProductDir$', 'gen')), | |
] | |
) | |
PushAssignment(bff, | |
@@ -953,8 +952,8 @@ def PushTargetForConfig( | |
PushAssignment( | |
bff, | |
'.CompilerOutputPath', | |
- '$ProductDir$/obj/%s' % | |
- target_name) | |
+ os.path.join('$ProductDir$', 'obj', target_name) | |
+ ) | |
config = target_spec.get('configurations').get(config_name, {}) | |
@@ -1016,7 +1015,7 @@ def PushTargetForConfig( | |
using_config() | |
PushAssignment( | |
- bff, '.LinkerOutput', '$ProductDir$/%s' % linker_product | |
+ bff, '.LinkerOutput', os.path.join('$ProductDir$', linker_product) | |
) | |
linker_options = MakeLinkerOptions(platform) | |
LinkerOptionsConfigure( | |
@@ -1071,14 +1070,14 @@ def PushTargetForConfig( | |
PushAssignment( | |
bff, | |
'.LibrarianOutput', | |
- '$ProductDir$/%s' % output_filename | |
+ os.path.join('$ProductDir$', output_filename) | |
) | |
PushAssignment(bff, | |
'.LibrarianAdditionalInputs', | |
bff_all_object_list_names) | |
PushAssignment(bff, | |
'.CompilerOutputPath', | |
- '$ProductDir$/lib/%s' % target_name) | |
+ os.path.join('$ProductDir$', 'lib', target_name)) | |
bff_product_target_name = library_name | |
elif target_type == 'none': | |
-- | |
2.7.0.windows.1 | |
From 21c2ff6d4053d07c23c611e18287ec41ae98ee76 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Fri, 15 Jul 2016 10:24:19 +0200 | |
Subject: [PATCH 16/19] Unify some aspects of command line generation | |
--- | |
pylib/gyp/generator/fastbuild.py | 72 +++++++++++++++------------------------- | |
1 file changed, 27 insertions(+), 45 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index ecfbecc..1ce8c10 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -524,10 +524,6 @@ def CompilerOptionsPushIncludeDir(platform, compiler_options, include_dir): | |
platform.push_include_dir(compiler_options, include_dir) | |
-def CompilerOptionsHasCommandLine(compiler_options): | |
- return len(compiler_options) > 0 | |
- | |
- | |
def CompilerOptionsConfigure(platform, compiler_options, config_name): | |
# TODO(nicolas): take language into account | |
platform.push_specific_cflags(compiler_options, config_name) | |
@@ -537,13 +533,13 @@ def CompilerOptionsConfigure(platform, compiler_options, config_name): | |
] | |
compiler_options[:] = new_compiler_options | |
-def PlatformGetCommandLine(platform, options): | |
- return ' '.join([PlatformQuoteArgument(platform, x) for x in options]) | |
+def PlatformHasCommandLine(options): | |
+ return len(options) > 0 | |
-def CompilerOptionsGetCommandLine(platform, compiler_options): | |
- return PlatformGetCommandLine(platform, compiler_options) | |
+def PlatformGetCommandLine(platform, options): | |
+ return ' '.join([PlatformQuoteArgument(platform, x) for x in options]) | |
def MakeLinkerOptions(platform): | |
return [] | |
@@ -562,10 +558,6 @@ def LinkerOptionsHasCommandLine(linker_options): | |
return len(linker_options) > 0 | |
-def LinkerOptionsGetCommandLine(platform, linker_options): | |
- return PlatformGetCommandLine(platform, linker_options) | |
- | |
- | |
# TODO(nicolas): what about moving that to linker options function | |
def PlatformLDFlagsForLibraryDirs(platform, library_dirs): | |
return platform.ldflags_for_library_dirs(library_dirs) | |
@@ -698,6 +690,20 @@ def PushTargetSpecs( | |
PushAlias(bff, target_name, all_bff_target_names) | |
+def PushCommandLineConcatenation(bff, variable_name, platform, command_line_options, comment=None): | |
+ # TODO(nicolas): see if we can support this in BFFWriteToFile w/ a wrap-mode | |
+ # for making it more readable. | |
+ if PlatformHasCommandLine(command_line_options): | |
+ if comment is not None: | |
+ PushComment(bff, comment) | |
+ PushCommand( | |
+ bff, | |
+ BFFConcatenation( | |
+ variable_name, | |
+ ' ' + PlatformGetCommandLine(platform, command_line_options) | |
+ ) | |
+ ) | |
+ | |
def PushTargetForConfig( | |
bff, | |
output_filename, | |
@@ -910,18 +916,12 @@ def PushTargetForConfig( | |
PushAssignment(bff, '.CompilerInputFiles', os_sources) | |
compiler_options = MakeCompilerOptions(platform) | |
CompilerOptionsConfigure(platform, compiler_options, config_name) | |
- if CompilerOptionsHasCommandLine(compiler_options): | |
- # TODO(nicolas): I wonder if we could de-duplicate those | |
- # into structs, for more readability | |
- PushComment(bff, 'Config-specific command line') | |
- PushCommand( | |
- bff, | |
- BFFConcatenation( | |
- '.CompilerOptions', | |
- ' ' + CompilerOptionsGetCommandLine(platform, compiler_options), | |
- ) | |
- ) | |
- | |
+ # TODO(nicolas): I wonder if we could de-duplicate those | |
+ # into structs, for more readability | |
+ PushCommandLineConcatenation( | |
+ bff, '.CompilerOptions', platform, compiler_options, | |
+ comment='Config-specific command line' | |
+ ) | |
config = target_spec.get('configurations').get(config_name, {}) | |
compiler_options = MakeCompilerOptions(platform) | |
for define in config.get('defines', []): | |
@@ -937,18 +937,8 @@ def PushTargetForConfig( | |
include_dir if os.path.isabs( | |
include_dir) else gyp_path_to_os_path(include_dir) | |
) | |
- if CompilerOptionsHasCommandLine(compiler_options): | |
- PushComment( | |
- bff, | |
- 'Target-specific command line' | |
- ) | |
- PushCommand( | |
- bff, | |
- BFFConcatenation( | |
- '.CompilerOptions', | |
- ' ' + CompilerOptionsGetCommandLine(platform, compiler_options) | |
- ) | |
- ) | |
+ PushCommandLineConcatenation(bff, '.CompilerOptions', platform, compiler_options, | |
+ comment='Target-specific command line') | |
PushAssignment( | |
bff, | |
'.CompilerOutputPath', | |
@@ -1026,15 +1016,7 @@ def PushTargetForConfig( | |
target_type, | |
linker_product | |
) | |
- if LinkerOptionsHasCommandLine(linker_options): | |
- PushCommand( | |
- bff, | |
- BFFConcatenation( | |
- '.LinkerOptions', | |
- ' ' + LinkerOptionsGetCommandLine(platform, linker_options) | |
- ) | |
- ) | |
- | |
+ PushCommandLineConcatenation(bff, '.LinkerOptions', platform, linker_options) | |
for flag in PlatformLDFlagsForLibraryDirs( | |
platform, config.get('library_dirs', [])): | |
PushCommand( | |
-- | |
2.7.0.windows.1 | |
From 0f2eb412cd8c6bef39ab5b1d95415df5d1ded9bc Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Fri, 15 Jul 2016 10:24:35 +0200 | |
Subject: [PATCH 17/19] Start studying what's happening with manifest files | |
--- | |
pylib/gyp/generator/fastbuild.py | 11 +++++++++-- | |
1 file changed, 9 insertions(+), 2 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index 1ce8c10..cd5080a 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -4,6 +4,11 @@ | |
# This module generates `.bff` files for consumption with the FASTbuild | |
# URL(http://fastbuild.org) opensource build system developed by Franta Fulin. | |
# | |
+# TODO(nicolas): a `generated.manifest` file gets generated by the msvs emulation, | |
+# yet it does not appear in our `.bff` file. What's up with that? Furthermore the | |
+# bff file we generate tends to produce an `intermediate.manifest` file in the | |
+# current working directory of the build. | |
+# | |
# TODO(nicolas): Where should PlatformExpandMacros actually be called? | |
# Every gyp input variable? | |
# | |
@@ -20,6 +25,8 @@ | |
# Try removing the Aliases entirely. Would the generated bff file work | |
# at all without them. And if not, why not? | |
# | |
+# i.e. Use PreBuildDepedencies and actual build products instead! | |
+# | |
import copy | |
import gyp | |
@@ -475,8 +482,8 @@ def MakePlatform(flavor, generator_flags, toolset, toplevel_dir, target_spec): | |
def gyp_to_build_path(x): | |
return PlatformGetPathFromGypPath(self, inputs_base_dir, x) | |
def expand_special(path, product_dir=None): | |
- return "EXPAND_SPECIAL(%s, %s)" % (path, product_dir) | |
- manifest_base_name = "MANIFEST_BASE_NAME" | |
+ return "SENTINEL.EXPAND_SPECIAL(%s, %s)" % (path, product_dir) | |
+ manifest_base_name = "SENTINEL." + target_output_filename | |
output_name = "$ProductDir$/%s" % target_output_filename | |
is_executable = target_type == 'executable' | |
-- | |
2.7.0.windows.1 | |
From feb98d31d66d9886c3c263c6864e996babc73887 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Fri, 15 Jul 2016 10:45:02 +0200 | |
Subject: [PATCH 18/19] Add TODO | |
--- | |
pylib/gyp/generator/fastbuild.py | 4 +++- | |
1 file changed, 3 insertions(+), 1 deletion(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index cd5080a..e5f07cf 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -7,7 +7,7 @@ | |
# TODO(nicolas): a `generated.manifest` file gets generated by the msvs emulation, | |
# yet it does not appear in our `.bff` file. What's up with that? Furthermore the | |
# bff file we generate tends to produce an `intermediate.manifest` file in the | |
-# current working directory of the build. | |
+# current working directory of the build. | |
# | |
# TODO(nicolas): Where should PlatformExpandMacros actually be called? | |
# Every gyp input variable? | |
@@ -499,6 +499,8 @@ def MakePlatform(flavor, generator_flags, toolset, toplevel_dir, target_spec): | |
outputs_base_dir | |
) | |
options.extend(ldflags) | |
+ # TODO(nicolas): use the manifest_files listed here to generate | |
+ # the necessary commands. | |
def ldflags(self, libraries): | |
return self.msvs_settings.AdjustLibraries(libraries) | |
-- | |
2.7.0.windows.1 | |
From d69b3bba0ba2bd67892c7896046a37a4d41f6822 Mon Sep 17 00:00:00 2001 | |
From: nicolas <[email protected]> | |
Date: Mon, 18 Jul 2016 10:10:49 +0200 | |
Subject: [PATCH 19/19] Study a bit more the manifest situation | |
--- | |
pylib/gyp/generator/fastbuild.py | 33 +++++++++++++++++++++++++++++++-- | |
1 file changed, 31 insertions(+), 2 deletions(-) | |
diff --git a/pylib/gyp/generator/fastbuild.py b/pylib/gyp/generator/fastbuild.py | |
index e5f07cf..883a5c1 100644 | |
--- a/pylib/gyp/generator/fastbuild.py | |
+++ b/pylib/gyp/generator/fastbuild.py | |
@@ -7,7 +7,7 @@ | |
# TODO(nicolas): a `generated.manifest` file gets generated by the msvs emulation, | |
# yet it does not appear in our `.bff` file. What's up with that? Furthermore the | |
# bff file we generate tends to produce an `intermediate.manifest` file in the | |
-# current working directory of the build. | |
+# current working directory of the build. | |
# | |
# TODO(nicolas): Where should PlatformExpandMacros actually be called? | |
# Every gyp input variable? | |
@@ -27,6 +27,14 @@ | |
# | |
# i.e. Use PreBuildDepedencies and actual build products instead! | |
# | |
+# TODO(nicolas): converting explicit file rules into wildcards would | |
+# help reducing the amount of `.bff` file changes which in turn helps | |
+# FASTbuild not re-building everything needlessly over and over again. | |
+# see URL(https://github.com/fastbuild/fastbuild/issues/108#issuecomment-233198894) | |
+# | |
+# TODO(nicolas): check and mark all ignored values returned by emulation backends | |
+# | |
+# TODO(nicolas): should we use gyp-win-tool rather than the compiler and linker directly? | |
import copy | |
import gyp | |
@@ -58,7 +66,8 @@ def debug_assert(x): | |
# TODO(nil): shouldn't we also search in the runtime path of the toolset? | |
# for instance we have ml64.exe that needs to be found but that cannot be guaranteed | |
-# when launching the `gyp` command | |
+# when launching the `gyp` command. | |
+# On the other hand ml64.exe should really not be inside an Exec node and instead in an `ObjectList` w/ Compiler `ml64.exe` | |
def shell_which(filename, additional_paths=None): | |
if additional_paths is None: | |
additional_paths = [] | |
@@ -501,6 +510,18 @@ def MakePlatform(flavor, generator_flags, toolset, toplevel_dir, target_spec): | |
options.extend(ldflags) | |
# TODO(nicolas): use the manifest_files listed here to generate | |
# the necessary commands. | |
+ # URL(https://msdn.microsoft.com/en-us/library/aa375649(v=vs.85).aspx) MT.exe documentation | |
+ print "ignored intermediate manifest: %s, manifest files: %s " % (intermediate_manifest, manifest_files) | |
+ print "This should roughly do:" | |
+ print "mt -nologo -manifest %(manifests)s -out:%(out)s.manifest" | |
+ print "turn manifest into rc file (manually)" | |
+ # TODO(nicolas): the commands above will need some Exec(...) nodes and | |
+ # to add them to the dependencies for the executable being built. | |
+ # We will need to support .PreBuildDependencies btw for that. | |
+ # TODO(nicolas) Put .rc file into ObjectList w/ Compiler RC.exe | |
+ # this will require something like: .ResourceCompiler = '$WindowsSDKBasePath$\bin\x86\RC.exe' in our config | |
+ # rc %(out)s.manifest.rc | |
+ # and link to the resulting .res | |
def ldflags(self, libraries): | |
return self.msvs_settings.AdjustLibraries(libraries) | |
@@ -843,6 +864,14 @@ def PushTargetForConfig( | |
action_name = action_spec['action_name'] | |
bff_action_name = '%s-%s' % (bff_target_name, action_name) | |
PushComment(bff, 'Action %s' % action_name) | |
+ objects = [] | |
+ for output in action_spec['outputs']: | |
+ # TODO(nicolas): TAG(win32) TAG(platform) this should not be hardcoded here | |
+ if output.endswith(".obj"): | |
+ objects.append(output) | |
+ if objects: | |
+ PushComment(bff, 'Action contains object files, should it not be an ObjectList instead?') | |
+ PushComment(bff, 'Objects: %s' % objects) | |
apply_multiple_output_hack(action_spec) | |
-- | |
2.7.0.windows.1 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Settings | |
{ | |
.CachePath = '/tmp/FBCache' | |
} | |
Compiler('clangcxx-x64') | |
{ | |
.Executable = '/usr/bin/clang++' | |
} | |
Compiler('clangcc-x64') | |
{ | |
.Executable = '/usr/bin/clang' | |
} | |
.ClangCXXConfig_OSX = | |
[ | |
.Compiler = 'clangcxx-x64' | |
.CompilerOptions = '-c "%1" -o "%2"' | |
.CompilerOptions + ' -std=c++0x -stdlib=libc++' | |
;; .CompilerOptions + ' -mmacosx-version-min=10.7' | |
] | |
.TargetLinker = | |
[ | |
.Linker = '/usr/bin/clang++' | |
.LinkerOptions = '"%1" -o "%2"' | |
] | |
.TargetLibrarian = | |
[ | |
.Librarian = '/usr/bin/ar' | |
.LibrarianOptions = 'rcs "%2" "%1"' | |
] | |
.TargetCXXCompiler = .ClangCXXConfig_OSX | |
.TargetCCompiler = | |
[ | |
.Compiler = 'clangcc-x64' | |
.CompilerOptions = '-c "%1" -o "%2"' | |
] | |
.TargetObjCCompiler = | |
[ | |
.Compiler = 'clangcc-x64' | |
.CompilerOptions = '-c "%1" -o "%2"' | |
] | |
.TargetObjCXXCompiler = | |
[ | |
.Compiler = 'clangcxx-x64' | |
.CompilerOptions = '-c "%1" -o "%2"' | |
] | |
.TargetDebugCXXCompiler = | |
[ | |
Using(.TargetCXXCompiler) | |
;; .CompilerOptions + ' -g -O1' | |
] | |
.TargetReleaseCXXCompiler = | |
[ | |
Using(.TargetCXXCompiler) | |
;; .CompilerOptions + ' -O2' | |
] | |
.TargetDebugCCompiler = .TargetCCompiler | |
.TargetReleaseCCompiler = .TargetCCompiler | |
.TargetDebugObjCCompiler = .TargetObjCCompiler | |
.TargetReleaseObjCCompiler = .TargetObjCCompiler | |
.TargetDebugObjCXXCompiler = .TargetObjCXXCompiler | |
.TargetReleaseObjCXXCompiler = .TargetObjCXXCompiler |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// One of the few references for a big project is the build file for FASTbuild itself, | |
// See URL( https://github.com/fastbuild/fastbuild/blob/master/Code/fbuild.bff ) | |
// | |
// Example: | |
// RUN(FASTBUILD_CACHE_PATH=m:/dev/FASTBUILDCache fbuild -config ../live.bff -cacheread -cachewrite) | |
;; | |
.QtDir = 'c:/GuiEnv_32/Qt5.5.0/msvc2013_32/' | |
.WindowsKitDir = 'C:\Program Files (x86)\Windows Kits\8.1' | |
#define VS140 | |
#if VS120 | |
#import VS120COMNTOOLS | |
.VSBasePath = '$VS120COMNTOOLS$\..\..' | |
Compiler('Compiler-msvc-x86') | |
{ | |
.CompilerOptions = '/nologo' | |
.CompilerOptions + ' /I"$VSBasePath$"\VC\INCLUDE' | |
.CompilerOptions + ' /I"$VSBasePath$"\VC\ATLMFC\INCLUDE' | |
.CompilerOptions + ' /I"$WindowsKitDir$"\include\shared' | |
.CompilerOptions + ' /I"$WindowsKitDir$"\include\um' | |
.Root = '$VSBasePath$\VC\Bin\' | |
.Executable = '$Root$\cl.exe' | |
.ExtraFiles = { | |
'$Root$\c1.dll' | |
'$Root$\c1ast.dll', | |
'$Root$\c1xx.dll', | |
'$Root$\c1xxast.dll', | |
'$Root$\c2.dll', | |
'$Root$\msobj120.dll' | |
'$Root$\mspdb120.dll' | |
'$Root$\mspdbsrv.exe' | |
'$Root$\mspdbcore.dll' | |
'$Root$\mspft120.dll' | |
'$Root$\1033\clui.dll' | |
'$VSBasePath$\VC\redist\x86\Microsoft.VC120.CRT\msvcp120.dll' | |
'$VSBasePath$\VC\redist\x86\Microsoft.VC120.CRT\msvcr120.dll' | |
'$VSBasePath$\VC\redist\x86\Microsoft.VC120.CRT\vccorlib120.dll' | |
} | |
} | |
#endif | |
#if VS140 | |
#import VS140COMNTOOLS | |
.VSBasePath = '$VS140COMNTOOLS$\..\..' | |
Compiler('Compiler-msvc-x64') | |
{ | |
.CompilerOptions = '/nologo' | |
+ ' /WIN64' | |
.CompilerOptions + ' /I"$VSBasePath$"\VC\INCLUDE' | |
.CompilerOptions + ' /I"$VSBasePath$"\VC\ATLMFC\INCLUDE' | |
.CompilerOptions + ' /I"$WindowsKitDir$"\include\shared' | |
.CompilerOptions + ' /I"$WindowsKitDir$"\include\um' | |
.Root = '$VSBasePath$\VC\Bin\' | |
.Executable = '$Root$\amd64\cl.exe' | |
.ExtraFiles = { | |
;; TODO(nil): to be defined | |
} | |
} | |
#endif | |
;;.Compiler = 'Compiler-msvc-x64' | |
.CompilerOptions = '"%1" /Fo"%2" /c /Z7 /EHsc /nologo' | |
.StrictCompilerOptions = [ | |
.CompilerOptions + ' /W4 /WX' | |
.CompilerOptions + ' /wd4512 /wd4505' | |
] | |
.TargetLinker = [ | |
.Linker = '$VSBasePath$\VC\Bin\link.exe' | |
.LinkerOptions = ' /WX /NOLOGO /INCREMENTAL:NO /OUT:"%2" "%1" /DEBUG' | |
+ ' /MACHINE:x64' | |
+ ' /IGNORE:4001' ; don't complain about linking libs only | |
] | |
.TargetLibrarian = [ | |
.Librarian = '$VSBasePath$\VC\Bin\lib.exe' | |
.LibrarianOptions = '/OUT:"%2" "%1"' | |
] | |
.TargetDefault_x64CXXCompiler = [ | |
;; don't bother with this right now | |
.Compiler = 'Compiler-msvc-x64' | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment