def __init__(self, target_outputs, base_dir, build_dir, output_file): """ base_dir: path from source root to directory containing this gyp file, by gyp semantics, all input paths are relative to this build_dir: path from source root to build output """ self.target_outputs = target_outputs self.base_dir = base_dir self.build_dir = build_dir self.ninja = ninja_syntax.Writer(output_file) # Relative path from build output dir to base dir. self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir) # Relative path from base dir to build dir. self.base_to_build = os.path.join(InvertRelativePath(base_dir), build_dir)
def __init__(self, hash_for_rules, target_outputs, base_dir, build_dir, output_file, toplevel_build, output_file_name, flavor, toplevel_dir=None): """ base_dir: path from source root to directory containing this gyp file, by gyp semantics, all input paths are relative to this build_dir: path from source root to build output toplevel_dir: path to the toplevel directory """ self.hash_for_rules = hash_for_rules self.target_outputs = target_outputs self.base_dir = base_dir self.build_dir = build_dir self.ninja = ninja_syntax.Writer(output_file) self.toplevel_build = toplevel_build self.output_file_name = output_file_name self.flavor = flavor self.abs_build_dir = None if toplevel_dir is not None: self.abs_build_dir = os.path.abspath( os.path.join(toplevel_dir, build_dir)) self.obj_ext = '.obj' if flavor == 'win' else '.o' if flavor == 'win': # See docstring of msvs_emulation.GenerateEnvironmentFiles(). self.win_env = {} for arch in ('x86', 'x64'): self.win_env[arch] = 'environment.' + arch # Relative path from build output dir to base dir. build_to_top = gyp.common.InvertRelativePath(build_dir, toplevel_dir) self.build_to_base = os.path.join(build_to_top, base_dir) # Relative path from base dir to build dir. base_to_top = gyp.common.InvertRelativePath(base_dir, toplevel_dir) self.base_to_build = os.path.join(base_to_top, build_dir)
def __init__(self, target_outputs, base_dir, build_dir, output_file, flavor, abs_build_dir=None): """ base_dir: path from source root to directory containing this gyp file, by gyp semantics, all input paths are relative to this build_dir: path from source root to build output abs_build_dir: absolute path to the build directory """ self.target_outputs = target_outputs self.base_dir = base_dir self.build_dir = build_dir self.ninja = ninja_syntax.Writer(output_file) self.flavor = flavor self.abs_build_dir = abs_build_dir self.obj_ext = '.obj' if flavor == 'win' else '.o' # Relative path from build output dir to base dir. self.build_to_base = os.path.join(InvertRelativePath(build_dir), base_dir) # Relative path from base dir to build dir. self.base_to_build = os.path.join(InvertRelativePath(base_dir), build_dir)
def WriteSpec(self, spec, config_name, generator_flags): """The main entry point for NinjaWriter: write the build rules for a spec. Returns a Target object, which represents the output paths for this spec. Returns None if there are no outputs (e.g. a settings-only 'none' type target).""" self.config_name = config_name self.name = spec['target_name'] self.toolset = spec['toolset'] config = spec['configurations'][config_name] self.target = Target(spec['type']) self.is_standalone_static_library = bool( spec.get('standalone_static_library', 0)) # Track if this target contains any C++ files, to decide if gcc or g++ # should be used for linking. self.uses_cpp = False self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec) self.xcode_settings = self.msvs_settings = None if self.flavor == 'mac': self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec) if self.flavor == 'win': self.msvs_settings = gyp.msvs_emulation.MsvsSettings( spec, generator_flags) arch = self.msvs_settings.GetArch(config_name) self.ninja.variable('arch', self.win_env[arch]) self.ninja.variable('cc', '$cl_' + arch) self.ninja.variable('cxx', '$cl_' + arch) self.ninja.variable('cc_host', '$cl_' + arch) self.ninja.variable('cxx_host', '$cl_' + arch) self.ninja.variable('asm', '$ml_' + arch) if self.flavor == 'mac': self.archs = self.xcode_settings.GetActiveArchs(config_name) if len(self.archs) > 1: self.arch_subninjas = dict( (arch, ninja_syntax.Writer( OpenOutput( os.path.join(self.toplevel_build, self._SubninjaNameForArch(arch)), 'w'))) for arch in self.archs) # Compute predepends for all rules. # actions_depends is the dependencies this target depends on before running # any of its action/rule/copy steps. # compile_depends is the dependencies this target depends on before running # any of its compile steps. actions_depends = [] compile_depends = [] # TODO(evan): it is rather confusing which things are lists and which # are strings. Fix these. if 'dependencies' in spec: for dep in spec['dependencies']: if dep in self.target_outputs: target = self.target_outputs[dep] actions_depends.append(target.PreActionInput(self.flavor)) compile_depends.append(target.PreCompileInput()) actions_depends = filter(None, actions_depends) compile_depends = filter(None, compile_depends) actions_depends = self.WriteCollapsedDependencies( 'actions_depends', actions_depends) compile_depends = self.WriteCollapsedDependencies( 'compile_depends', compile_depends) self.target.preaction_stamp = actions_depends self.target.precompile_stamp = compile_depends # Write out actions, rules, and copies. These must happen before we # compile any sources, so compute a list of predependencies for sources # while we do it. extra_sources = [] mac_bundle_depends = [] self.target.actions_stamp = self.WriteActionsRulesCopies( spec, extra_sources, actions_depends, mac_bundle_depends) # If we have actions/rules/copies, we depend directly on those, but # otherwise we depend on dependent target's actions/rules/copies etc. # We never need to explicitly depend on previous target's link steps, # because no compile ever depends on them. compile_depends_stamp = (self.target.actions_stamp or compile_depends) # Write out the compilation steps, if any. link_deps = [] sources = extra_sources + spec.get('sources', []) if sources: if self.flavor == 'mac' and len(self.archs) > 1: # Write subninja file containing compile and link commands scoped to # a single arch if a fat binary is being built. for arch in self.archs: self.ninja.subninja(self._SubninjaNameForArch(arch)) pch = None if self.flavor == 'win': gyp.msvs_emulation.VerifyMissingSources( sources, self.abs_build_dir, generator_flags, self.GypPathToNinja) pch = gyp.msvs_emulation.PrecompiledHeader( self.msvs_settings, config_name, self.GypPathToNinja, self.GypPathToUniqueOutput, self.obj_ext) else: pch = gyp.xcode_emulation.MacPrefixHeader( self.xcode_settings, self.GypPathToNinja, lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang)) link_deps = self.WriteSources(self.ninja, config_name, config, sources, compile_depends_stamp, pch, spec) # Some actions/rules output 'sources' that are already object files. obj_outputs = [f for f in sources if f.endswith(self.obj_ext)] if obj_outputs: if self.flavor != 'mac' or len(self.archs) == 1: link_deps += [self.GypPathToNinja(o) for o in obj_outputs] else: print "Warning: Actions/rules writing object files don't work with " \ "multiarch targets, dropping. (target %s)" % spec['target_name'] elif self.flavor == 'mac' and len(self.archs) > 1: link_deps = collections.defaultdict(list) compile_deps = self.target.actions_stamp or actions_depends if self.flavor == 'win' and self.target.type == 'static_library': self.target.component_objs = link_deps self.target.compile_deps = compile_deps # Write out a link step, if needed. output = None is_empty_bundle = not link_deps and not mac_bundle_depends if link_deps or self.target.actions_stamp or actions_depends: output = self.WriteTarget(spec, config_name, config, link_deps, compile_deps) if self.is_mac_bundle: mac_bundle_depends.append(output) # Bundle all of the above together, if needed. if self.is_mac_bundle: output = self.WriteMacBundle(spec, mac_bundle_depends, is_empty_bundle) if not output: return None assert self.target.FinalOutput(), output return self.target
def GenerateOutput(target_list, target_dicts, data, params): options = params['options'] generator_flags = params.get('generator_flags', {}) if options.generator_output: raise NotImplementedError, "--generator_output not implemented for ninja" config_name = generator_flags.get('config', None) if config_name is None: # Guess which config we want to use: pick the first one from the # first target. config_name = target_dicts[target_list[0]]['default_configuration'] # builddir: relative path from source root to our output files. # e.g. "out/Debug" builddir = os.path.join(generator_flags.get('output_dir', 'out'), config_name) master_ninja = ninja_syntax.Writer( OpenOutput(os.path.join(options.toplevel_dir, builddir, 'build.ninja')), width=120) # TODO: compute cc/cxx/ld/etc. by command-line arguments and system tests. master_ninja.variable('cc', os.environ.get('CC', 'gcc')) master_ninja.variable('cxx', os.environ.get('CXX', 'g++')) master_ninja.variable('ld', '$cxx -Wl,--threads -Wl,--thread-count=4') master_ninja.variable('cc_host', '$cc') master_ninja.variable('cxx_host', '$cxx') master_ninja.newline() master_ninja.rule( 'cc', description='CC $out', command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c ' '-c $in -o $out'), depfile='$out.d') master_ninja.rule( 'cxx', description='CXX $out', command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc ' '-c $in -o $out'), depfile='$out.d') master_ninja.rule( 'alink', description='AR $out', command='rm -f $out && ar rcsT $out $in') master_ninja.rule( 'solink', description='SOLINK $out', command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' '-Wl,--whole-archive $in -Wl,--no-whole-archive $libs')) master_ninja.rule( 'link', description='LINK $out', command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib ' '-Wl,--start-group $in -Wl,--end-group $libs')) master_ninja.rule( 'stamp', description='STAMP $out', command='touch $out') master_ninja.rule( 'copy', description='COPY $in $out', command='ln -f $in $out 2>/dev/null || cp -af $in $out') master_ninja.newline() all_targets = set() for build_file in params['build_files']: for target in gyp.common.AllTargets(target_list, target_dicts, build_file): all_targets.add(target) all_outputs = set() target_outputs = {} for qualified_target in target_list: # qualified_target is like: third_party/icu/icu.gyp:icui18n#target build_file, name, toolset = \ gyp.common.ParseQualifiedTarget(qualified_target) # TODO: what is options.depth and how is it different than # options.toplevel_dir? build_file = gyp.common.RelativePath(build_file, options.depth) base_path = os.path.dirname(build_file) obj = 'obj' if toolset != 'target': obj += '.' + toolset output_file = os.path.join(obj, base_path, name + '.ninja') spec = target_dicts[qualified_target] config = spec['configurations'][config_name] writer = NinjaWriter(target_outputs, base_path, builddir, OpenOutput(os.path.join(options.toplevel_dir, builddir, output_file))) master_ninja.subninja(output_file) output = writer.WriteSpec(spec, config) if output: linkable = spec['type'] in ('static_library', 'shared_library') target_outputs[qualified_target] = (output, linkable) if qualified_target in all_targets: all_outputs.add(output) if all_outputs: master_ninja.build('all', 'phony', list(all_outputs))
def GenerateOutputForConfig(target_list, target_dicts, data, params, config_name): options = params['options'] flavor = gyp.common.GetFlavor(params) generator_flags = params.get('generator_flags', {}) # build_dir: relative path from source root to our output files. # e.g. "out/Debug" build_dir = os.path.join(generator_flags.get('output_dir', 'out'), config_name) master_ninja = ninja_syntax.Writer( OpenOutput(os.path.join(options.toplevel_dir, build_dir, 'build.ninja')), width=120) # Put build-time support tools in out/{config_name}. gyp.common.CopyTool(flavor, os.path.join(options.toplevel_dir, build_dir)) # Grab make settings for CC/CXX. if flavor == 'win': cc = cxx = 'cl' else: cc, cxx = 'gcc', 'g++' build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0]) make_global_settings = data[build_file].get('make_global_settings', []) build_to_root = InvertRelativePath(build_dir) for key, value in make_global_settings: if key == 'CC': cc = os.path.join(build_to_root, value) if key == 'CXX': cxx = os.path.join(build_to_root, value) flock = 'flock' if flavor == 'mac': flock = './gyp-mac-tool flock' master_ninja.variable('cc', os.environ.get('CC', cc)) master_ninja.variable('cxx', os.environ.get('CXX', cxx)) if flavor == 'win': master_ninja.variable('ld', 'link') else: master_ninja.variable('ld', flock + ' linker.lock $cxx') master_ninja.variable('cc_host', '$cc') master_ninja.variable('cxx_host', '$cxx') if flavor == 'mac': master_ninja.variable('mac_tool', os.path.join('.', 'gyp-mac-tool')) master_ninja.newline() if flavor != 'win': master_ninja.rule( 'cc', description='CC $out', command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c ' '$cflags_pch_c -c $in -o $out'), depfile='$out.d') master_ninja.rule( 'cxx', description='CXX $out', command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc ' '$cflags_pch_cc -c $in -o $out'), depfile='$out.d') else: # TODO(scottmg): Decide how /showIncludes handling should work in ninja. master_ninja.rule( 'cc', description='CC $out', command=('$cc /nologo $defines $includes $cflags $cflags_c ' '$cflags_pch_c /c $in /Fo$out'), depfile='$out.d') master_ninja.rule( 'cxx', description='CXX $out', command=('$cxx /nologo $defines $includes $cflags $cflags_cc ' '$cflags_pch_cc /c $in /Fo$out'), depfile='$out.d') if flavor != 'mac' and flavor != 'win': master_ninja.rule( 'alink', description='AR $out', command='rm -f $out && ar rcsT $out $in') master_ninja.rule( 'solink', description='SOLINK $out', command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' '-Wl,--whole-archive $in -Wl,--no-whole-archive $libs')) master_ninja.rule( 'solink_module', description='SOLINK(module) $out', command=('$ld -shared $ldflags -o $out -Wl,-soname=$soname ' '-Wl,--start-group $in -Wl,--end-group $libs')) master_ninja.rule( 'link', description='LINK $out', command=('$ld $ldflags -o $out -Wl,-rpath=\$$ORIGIN/lib ' '-Wl,--start-group $in -Wl,--end-group $libs')) elif flavor == 'win': master_ninja.rule( 'alink', description='AR $out', command='lib /nologo /OUT:$out.lib $in') master_ninja.rule( 'solink', description='SOLINK $out', command=('$ld /nologo /DLL $ldflags /OUT:$out.dll $in $libs')) master_ninja.rule( 'solink_module', description='SOLINK(module) $out', command=('$ld /nologo /DLL $ldflags /OUT:$out.dll $in $libs')) master_ninja.rule( 'link', description='LINK $out', command=('$ld /nologo $ldflags /OUT:$out.exe $in $libs')) else: master_ninja.rule( 'objc', description='OBJC $out', command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc ' '$cflags_pch_objc -c $in -o $out'), depfile='$out.d') master_ninja.rule( 'objcxx', description='OBJCXX $out', command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc ' '$cflags_pch_objcc -c $in -o $out'), depfile='$out.d') master_ninja.rule( 'alink', description='LIBTOOL-STATIC $out, POSTBUILDS', command='rm -f $out && ' './gyp-mac-tool filter-libtool libtool -static -o $out $in' '$postbuilds') # TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass # -bundle -single_module here (for osmesa.so). master_ninja.rule( 'solink', description='SOLINK $out, POSTBUILDS', command=('$ld -shared $ldflags -o $out ' '$in $libs$postbuilds')) master_ninja.rule( 'solink_module', description='SOLINK(module) $out, POSTBUILDS', command=('$ld -shared $ldflags -o $out ' '$in $libs$postbuilds')) master_ninja.rule( 'link', description='LINK $out, POSTBUILDS', command=('$ld $ldflags -o $out ' '$in $libs$postbuilds')) master_ninja.rule( 'infoplist', description='INFOPLIST $out', command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && ' 'plutil -convert xml1 $out $out')) master_ninja.rule( 'mac_tool', description='MACTOOL $mactool_cmd $in', command='$env $mac_tool $mactool_cmd $in $out') master_ninja.rule( 'package_framework', description='PACKAGE FRAMEWORK $out, POSTBUILDS', command='$mac_tool package-framework $out $version$postbuilds ' '&& touch $out') master_ninja.rule( 'stamp', description='STAMP $out', command='${postbuilds}touch $out') if flavor == 'win': # TODO(scottmg): Copy fallback? master_ninja.rule( 'copy', description='COPY $in $out', command='mklink /h $out $in >nul') else: master_ninja.rule( 'copy', description='COPY $in $out', command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)') master_ninja.newline() all_targets = set() for build_file in params['build_files']: for target in gyp.common.AllTargets(target_list, target_dicts, build_file): all_targets.add(target) all_outputs = set() # target_outputs is a map from qualified target name to a Target object. target_outputs = {} for qualified_target in target_list: # qualified_target is like: third_party/icu/icu.gyp:icui18n#target build_file, name, toolset = \ gyp.common.ParseQualifiedTarget(qualified_target) this_make_global_settings = data[build_file].get('make_global_settings', []) assert make_global_settings == this_make_global_settings, ( "make_global_settings needs to be the same for all targets.") spec = target_dicts[qualified_target] if flavor == 'mac': gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec) build_file = gyp.common.RelativePath(build_file, options.toplevel_dir) base_path = os.path.dirname(build_file) obj = 'obj' if toolset != 'target': obj += '.' + toolset output_file = os.path.join(obj, base_path, name + '.ninja') abs_build_dir=os.path.abspath(os.path.join(options.toplevel_dir, build_dir)) writer = NinjaWriter(target_outputs, base_path, build_dir, OpenOutput(os.path.join(options.toplevel_dir, build_dir, output_file)), flavor, abs_build_dir=abs_build_dir) master_ninja.subninja(output_file) target = writer.WriteSpec(spec, config_name) if target: target_outputs[qualified_target] = target if qualified_target in all_targets: all_outputs.add(target.FinalOutput()) if all_outputs: master_ninja.build('all', 'phony', list(all_outputs))
def __init__(self, target_outputs, base_dir, output_file): self.target_outputs = target_outputs # The root-relative path to the source .gyp file; by gyp # semantics, all input paths are relative to this. self.base_dir = base_dir self.ninja = ninja_syntax.Writer(output_file)