mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Add support for scons ninja backend
This commit is contained in:
44
SConstruct
44
SConstruct
@@ -60,6 +60,11 @@ The following environment variables modify the build environment:
|
|||||||
OPENSSL_ROOT
|
OPENSSL_ROOT
|
||||||
Path to the openssl directory.
|
Path to the openssl directory.
|
||||||
|
|
||||||
|
The following extra options may be used:
|
||||||
|
--ninja Generate a `build.ninja` build file for the specified target
|
||||||
|
(see: https://martine.github.io/ninja/). Only gcc and clang targets
|
||||||
|
are supported.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
#
|
#
|
||||||
'''
|
'''
|
||||||
@@ -83,11 +88,16 @@ import time
|
|||||||
import SCons.Action
|
import SCons.Action
|
||||||
|
|
||||||
sys.path.append(os.path.join('src', 'beast', 'site_scons'))
|
sys.path.append(os.path.join('src', 'beast', 'site_scons'))
|
||||||
|
sys.path.append(os.path.join('src', 'ripple', 'site_scons'))
|
||||||
|
|
||||||
import Beast
|
import Beast
|
||||||
|
import scons_to_ninja
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
AddOption('--ninja', dest='ninja', action='store_true',
|
||||||
|
help='generate ninja build file build.ninja')
|
||||||
|
|
||||||
def parse_time(t):
|
def parse_time(t):
|
||||||
return time.strptime(t, '%a %b %d %H:%M:%S %Z %Y')
|
return time.strptime(t, '%a %b %d %H:%M:%S %Z %Y')
|
||||||
|
|
||||||
@@ -591,6 +601,7 @@ class ObjectBuilder(object):
|
|||||||
self.env = env
|
self.env = env
|
||||||
self.variant_dirs = variant_dirs
|
self.variant_dirs = variant_dirs
|
||||||
self.objects = []
|
self.objects = []
|
||||||
|
self.child_envs = []
|
||||||
|
|
||||||
def add_source_files(self, *filenames, **kwds):
|
def add_source_files(self, *filenames, **kwds):
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
@@ -598,6 +609,7 @@ class ObjectBuilder(object):
|
|||||||
if kwds:
|
if kwds:
|
||||||
env = env.Clone()
|
env = env.Clone()
|
||||||
env.Prepend(**kwds)
|
env.Prepend(**kwds)
|
||||||
|
self.child_envs.append(env)
|
||||||
o = env.Object(Beast.variantFile(filename, self.variant_dirs))
|
o = env.Object(Beast.variantFile(filename, self.variant_dirs))
|
||||||
self.objects.append(o)
|
self.objects.append(o)
|
||||||
|
|
||||||
@@ -733,6 +745,31 @@ def should_prepare_targets(style, toolchain, variant):
|
|||||||
if should_prepare_target(t, style, toolchain, variant):
|
if should_prepare_target(t, style, toolchain, variant):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def should_build_ninja(style, toolchain, variant):
|
||||||
|
"""
|
||||||
|
Return True if a ninja build file should be generated.
|
||||||
|
|
||||||
|
Typically, scons will be called as follows to generate a ninja build file:
|
||||||
|
`scons ninja=1 gcc.debug` where `gcc.debug` may be replaced with any of our
|
||||||
|
non-visual studio targets. Raise an exception if we cannot generate the
|
||||||
|
requested ninja build file (for example, if multiple targets are requested).
|
||||||
|
"""
|
||||||
|
if not GetOption('ninja'):
|
||||||
|
return False
|
||||||
|
if len(COMMAND_LINE_TARGETS) != 1:
|
||||||
|
raise Exception('Can only generate a ninja file for a single target')
|
||||||
|
cl_target = COMMAND_LINE_TARGETS[0]
|
||||||
|
if 'vcxproj' in cl_target:
|
||||||
|
raise Exception('Cannot generate a ninja file for a vcxproj')
|
||||||
|
s = cl_target.split('.')
|
||||||
|
if ( style == 'unity' and 'nounity' in s or
|
||||||
|
style == 'classic' and 'nounity' not in s or
|
||||||
|
len(s) == 1 ):
|
||||||
|
return False
|
||||||
|
if len(s) == 2 or len(s) == 3:
|
||||||
|
return s[0] == toolchain and s[1] == variant
|
||||||
|
return False
|
||||||
|
|
||||||
for tu_style in ['classic', 'unity']:
|
for tu_style in ['classic', 'unity']:
|
||||||
if tu_style == 'classic':
|
if tu_style == 'classic':
|
||||||
sources = get_classic_sources()
|
sources = get_classic_sources()
|
||||||
@@ -861,6 +898,13 @@ for tu_style in ['classic', 'unity']:
|
|||||||
aliases[variant].extend(target)
|
aliases[variant].extend(target)
|
||||||
env.Alias(variant_name, target)
|
env.Alias(variant_name, target)
|
||||||
|
|
||||||
|
# ninja support
|
||||||
|
if should_build_ninja(tu_style, toolchain, variant):
|
||||||
|
print('Generating ninja: {}:{}:{}'.format(tu_style, toolchain, variant))
|
||||||
|
scons_to_ninja.GenerateNinjaFile(
|
||||||
|
[object_builder.env] + object_builder.child_envs,
|
||||||
|
dest_file='build.ninja')
|
||||||
|
|
||||||
for key, value in aliases.iteritems():
|
for key, value in aliases.iteritems():
|
||||||
env.Alias(key, value)
|
env.Alias(key, value)
|
||||||
|
|
||||||
|
|||||||
85
src/ripple/site_scons/scons_to_ninja.py
Normal file
85
src/ripple/site_scons/scons_to_ninja.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Copyright (c) 2014 The Native Client Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
|
# found in the LICENSE file.
|
||||||
|
import atexit
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import SCons.Action
|
||||||
|
# This implements a Ninja backend for SCons. This allows SCons to be used
|
||||||
|
# as a Ninja file generator, similar to how Gyp will generate Ninja files.
|
||||||
|
#
|
||||||
|
# This is a way to bypass SCons's slow startup time. After running SCons
|
||||||
|
# to generate a Ninja file (which is fairly slow), you can rebuild targets
|
||||||
|
# quickly using Ninja, as long as the .scons files haven't changed.
|
||||||
|
#
|
||||||
|
# The implementation is fairly hacky: It hooks PRINT_CMD_LINE_FUNC to
|
||||||
|
# discover the build commands that SCons would normally run.
|
||||||
|
#
|
||||||
|
# A cleaner implementation would traverse the node graph instead.
|
||||||
|
# Traversing the node graph is itself straightforward, but finding the
|
||||||
|
# command associated with a node is not -- I couldn't figure out how to do
|
||||||
|
# that.
|
||||||
|
# This is necessary to handle SCons's "variant dir" feature. The filename
|
||||||
|
# associated with a Scons node can be ambiguous: it might come from the
|
||||||
|
# build dir or the source dir.
|
||||||
|
def GetRealNode(node):
|
||||||
|
src = node.srcnode()
|
||||||
|
if src.stat() is not None:
|
||||||
|
return src
|
||||||
|
return node
|
||||||
|
def GenerateNinjaFile(envs, dest_file):
|
||||||
|
# Tell SCons not to run any commands, just report what would be run.
|
||||||
|
for e in envs:
|
||||||
|
e.SetOption('no_exec', True)
|
||||||
|
# Tell SCons that everything needs rebuilding.
|
||||||
|
e.Decider(lambda dependency, target, prev_ni: True)
|
||||||
|
# Use a list to ensure that the output is ordered deterministically.
|
||||||
|
node_list = []
|
||||||
|
node_map = {}
|
||||||
|
def CustomCommandPrinter(cmd, targets, source, env):
|
||||||
|
assert len(targets) == 1, len(targets)
|
||||||
|
node = targets[0]
|
||||||
|
# There can sometimes be multiple commands per target (e.g. ar+ranlib).
|
||||||
|
# We must collect these together to output a single Ninja rule.
|
||||||
|
if node not in node_map:
|
||||||
|
node_list.append(node)
|
||||||
|
node_map.setdefault(node, []).append(cmd)
|
||||||
|
for e in envs:
|
||||||
|
e.Append(PRINT_CMD_LINE_FUNC=CustomCommandPrinter)
|
||||||
|
def WriteFile():
|
||||||
|
dest_temp = '%s.tmp' % dest_file
|
||||||
|
ninja_fh = open(dest_temp, 'w')
|
||||||
|
ninja_fh.write("""\
|
||||||
|
# Generated by scons_to_ninja.py
|
||||||
|
# Generic rule for handling any command.
|
||||||
|
rule cmd
|
||||||
|
command = $cmd
|
||||||
|
# NaCl overrides SCons's Install() step to create hard links, for speed.
|
||||||
|
# To coexist with that, we must remove the file before copying, otherwise
|
||||||
|
# cp complains the source and dest "are the same file". We also create
|
||||||
|
# hard links here (with -l) for speed.
|
||||||
|
rule install
|
||||||
|
command = rm -f $out && cp -l $in $out
|
||||||
|
""")
|
||||||
|
for node in node_list:
|
||||||
|
dest_path = node.get_path()
|
||||||
|
cmds = node_map[node]
|
||||||
|
deps = [GetRealNode(dep).get_path() for dep in node.all_children()]
|
||||||
|
action = node.builder.action
|
||||||
|
if type(action) == SCons.Action.FunctionAction:
|
||||||
|
funcname = action.function_name()
|
||||||
|
if funcname == 'installFunc':
|
||||||
|
assert len(deps) == 1, len(deps)
|
||||||
|
ninja_fh.write('\nbuild %s: install %s\n'
|
||||||
|
% (dest_path, ' '.join(deps)))
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
sys.stderr.write('Unknown FunctionAction, %r: skipping target %r\n'
|
||||||
|
% (funcname, dest_path))
|
||||||
|
continue
|
||||||
|
ninja_fh.write('\nbuild %s: cmd %s\n'
|
||||||
|
% (dest_path, ' '.join(deps)))
|
||||||
|
ninja_fh.write(' cmd = %s\n' % ' && '.join(cmds))
|
||||||
|
# Make the result file visible atomically.
|
||||||
|
os.rename(dest_temp, dest_file)
|
||||||
|
atexit.register(WriteFile)
|
||||||
Reference in New Issue
Block a user