@ -0,0 +1,25 @@ | |||||
Copyright and license for SCons - a software construction tool | |||||
This copyright and license do not apply to any other software | |||||
with which this software may have been included. | |||||
Copyright (c) 2001 - 2017 The SCons Foundation | |||||
Permission is hereby granted, free of charge, to any person obtaining | |||||
a copy of this software and associated documentation files (the | |||||
"Software"), to deal in the Software without restriction, including | |||||
without limitation the rights to use, copy, modify, merge, publish, | |||||
distribute, sublicense, and/or sell copies of the Software, and to | |||||
permit persons to whom the Software is furnished to do so, subject to | |||||
the following conditions: | |||||
The above copyright notice and this permission notice shall be included | |||||
in all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,205 @@ | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
SCons - a software construction tool | |||||
This is the scons-README file for a version of SCons packaged for local | |||||
execution--that is, execution out of a specific local directory, without | |||||
having to install SCons as a system-wide utility. | |||||
You are likely reading this file in one of the following two situations: | |||||
1) You have unpacked an scons-local-{version} package and are | |||||
examining the contents. | |||||
In this case, you are presumably interested in using this | |||||
package to include a local copy of SCons with some other | |||||
software that you package, so that you can use SCons to build | |||||
your software without forcing all of your users to have it fully | |||||
installed. Instructions for this can be found below. | |||||
If you are not looking to use SCons in this way, then please | |||||
use either the scons-{version} package to install SCons on your | |||||
system, or the scons-src-{version} package if you want the full | |||||
source to SCons, including its packaging code and underlying | |||||
tests and testing infrastructure. | |||||
2) This file was included in some other software package so that | |||||
the package could be built using SCons. | |||||
In this case, follow the instructions provided with the | |||||
rest of the software package for how to use SCons to build | |||||
and/or install the software. The file containing build and | |||||
installation instructions will typically be named README or | |||||
INSTALL. | |||||
LATEST VERSION | |||||
============== | |||||
Before going further, you can check for the latest version of the | |||||
scons-local package, or any SCons package, at the SCons download page: | |||||
http://www.scons.org/download.html | |||||
EXECUTION REQUIREMENTS | |||||
====================== | |||||
Running SCons requires Python version 2.4 or later. There should be | |||||
no other dependencies or requirements to run SCons. | |||||
The default SCons configuration assumes use of the Microsoft Visual C++ | |||||
compiler suite on WIN32 systems, and assumes a C compiler named 'cc', | |||||
a C++ compiler named 'c++', and a Fortran compiler named 'g77' (such | |||||
as found in the GNU C compiler suite) on any other type of system. | |||||
You may, of course, override these default values by appropriate | |||||
configuration of Environment construction variables. | |||||
INSTALLATION | |||||
============ | |||||
Installation of this package should be as simple as unpacking the | |||||
archive (either .tar.gz or .zip) in any directory (top-level or a | |||||
subdirectory) within the software package with which you want to ship | |||||
SCons. | |||||
Once you have installed this package, you should write an SConstruct | |||||
file at the top level of your source tree to build your software as you | |||||
see fit. | |||||
Then modify the build/install instructions for your package to instruct | |||||
your users to execute SCons as follows (if you installed this package in | |||||
your top-level directory): | |||||
$ python scons.py | |||||
Or (if, for example, you installed this package in a subdirectory named | |||||
"scons"): | |||||
$ python scons/scons.py | |||||
That should be all you have to do. (If it isn't that simple, please let | |||||
us know!) | |||||
CONTENTS OF THIS PACKAGE | |||||
======================== | |||||
This scons-local package consists of the following: | |||||
scons-LICENSE | |||||
A copy of the copyright and terms under which SCons is | |||||
distributed (the Open Source Initiative-approved MIT license). | |||||
A disclaimer has been added to the beginning to make clear that | |||||
this license applies only to SCons, and not to any separate | |||||
software you've written with which you're planning to package | |||||
SCons. | |||||
scons-README | |||||
What you're looking at right now. | |||||
scons-local-{version}/ | |||||
The SCons build engine. This is structured as a Python | |||||
library. | |||||
scons.py | |||||
The SCons script itself. The script sets up the Python | |||||
sys.path variable to use the build engine found in the | |||||
scons-local-{version}/ directory in preference to any other | |||||
SCons build engine installed on your system. | |||||
DOCUMENTATION | |||||
============= | |||||
Because this package is intended to be included with other software by | |||||
experienced users, we have not included any SCons documentation in this | |||||
package (other than this scons-README file you're reading right now). | |||||
If, however, you need documentation about SCons, then consult any of the | |||||
following from the corresponding scons-{version} or scons-src-{version} | |||||
package: | |||||
The RELEASE.txt file (src/RELEASE.txt file in the | |||||
scons-src-{version} package), which contains notes about this | |||||
specific release, including known problems. | |||||
The CHANGES.txt file (src/CHANGES.txt file in the | |||||
scons-src-{version} package), which contains a list of changes | |||||
since the previous release. | |||||
The scons.1 man page (doc/man/scons.1 in the scons-src-{version} | |||||
package), which contains a section of small examples for getting | |||||
started using SCons. | |||||
Additional documentation for SCons is available at: | |||||
http://www.scons.org/doc.html | |||||
LICENSING | |||||
========= | |||||
SCons is distributed under the MIT license, a full copy of which is | |||||
available in the scons-LICENSE file in this package. The MIT license is | |||||
an approved Open Source license, which means: | |||||
This software is OSI Certified Open Source Software. OSI | |||||
Certified is a certification mark of the Open Source Initiative. | |||||
More information about OSI certifications and Open Source software is | |||||
available at: | |||||
http://www.opensource.org/ | |||||
REPORTING BUGS | |||||
============== | |||||
You can report bugs either by following the "Tracker - Bugs" link | |||||
on the SCons project page: | |||||
http://sourceforge.net/projects/scons/ | |||||
or by sending mail to the SCons developers mailing list: | |||||
scons-devel@lists.sourceforge.net | |||||
MAILING LISTS | |||||
============= | |||||
A mailing list for users of SCons is available. You may send questions | |||||
or comments to the list at: | |||||
scons-users@lists.sourceforge.net | |||||
You may subscribe to the scons-users mailing list at: | |||||
http://lists.sourceforge.net/lists/listinfo/scons-users | |||||
FOR MORE INFORMATION | |||||
==================== | |||||
Check the SCons web site at: | |||||
http://www.scons.org/ | |||||
AUTHOR INFO | |||||
=========== | |||||
Steven Knight | |||||
knight at baldmt dot com | |||||
http://www.baldmt.com/~knight/ | |||||
With plenty of help from the SCons Development team: | |||||
Chad Austin | |||||
Charles Crain | |||||
Steve Leblanc | |||||
Anthony Roach | |||||
Terrel Shumway | |||||
@ -0,0 +1,141 @@ | |||||
#! /usr/bin/env python | |||||
# | |||||
# SCons - a Software Constructor | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
from __future__ import print_function | |||||
__revision__ = "src/script/scons-configure-cache.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__version__ = "3.0.0" | |||||
__build__ = "rel_3.0.0:4395:8972f6a2f699" | |||||
__buildsys__ = "ubuntu-16" | |||||
__date__ = "2017/09/18 12:59:24" | |||||
__developer__ = "bdbaddog" | |||||
import argparse | |||||
import glob | |||||
import json | |||||
import os | |||||
def rearrange_cache_entries(current_prefix_len, new_prefix_len): | |||||
print('Changing prefix length from', current_prefix_len, 'to', new_prefix_len) | |||||
dirs = set() | |||||
old_dirs = set() | |||||
for file in glob.iglob(os.path.join('*', '*')): | |||||
name = os.path.basename(file) | |||||
dir = name[:current_prefix_len].upper() | |||||
if dir not in old_dirs: | |||||
print('Migrating', dir) | |||||
old_dirs.add(dir) | |||||
dir = name[:new_prefix_len].upper() | |||||
if dir not in dirs: | |||||
os.mkdir(dir) | |||||
dirs.add(dir) | |||||
os.rename(file, os.path.join(dir, name)) | |||||
# Now delete the original directories | |||||
for dir in old_dirs: | |||||
os.rmdir(dir) | |||||
# This dictionary should have one entry per entry in the cache config | |||||
# Each entry should have the following: | |||||
# implicit - (optional) This is to allow adding a new config entry and also | |||||
# changing the behaviour of the system at the same time. This | |||||
# indicates the value the config entry would have had if it had been | |||||
# specified. | |||||
# default - The value the config entry should have if it wasn't previously | |||||
# specified | |||||
# command-line - parameters to pass to ArgumentParser.add_argument | |||||
# converter - (optional) Function to call if it's necessary to do some work | |||||
# if this configuration entry changes | |||||
config_entries = { | |||||
'prefix_len' : { | |||||
'implicit' : 1, | |||||
'default' : 2 , | |||||
'command-line' : { | |||||
'help' : 'Length of cache file name used as subdirectory prefix', | |||||
'metavar' : '<number>', | |||||
'type' : int | |||||
}, | |||||
'converter' : rearrange_cache_entries | |||||
} | |||||
} | |||||
parser = argparse.ArgumentParser( | |||||
description = 'Modify the configuration of an scons cache directory', | |||||
epilog = ''' | |||||
Unless you specify an option, it will not be changed (if it is | |||||
already set in the cache config), or changed to an appropriate | |||||
default (it it is not set). | |||||
''' | |||||
) | |||||
parser.add_argument('cache-dir', help='Path to scons cache directory') | |||||
for param in config_entries: | |||||
parser.add_argument('--' + param.replace('_', '-'), | |||||
**config_entries[param]['command-line']) | |||||
parser.add_argument('--version', action='version', version='%(prog)s 1.0') | |||||
# Get the command line as a dict without any of the unspecified entries. | |||||
args = dict([x for x in vars(parser.parse_args()).items() if x[1]]) | |||||
# It seems somewhat strange to me, but positional arguments don't get the - | |||||
# in the name changed to _, whereas optional arguments do... | |||||
os.chdir(args['cache-dir']) | |||||
del args['cache-dir'] | |||||
if not os.path.exists('config'): | |||||
# Validate the only files in the directory are directories 0-9, a-f | |||||
expected = [ '{:X}'.format(x) for x in range(0, 16) ] | |||||
if not set(os.listdir('.')).issubset(expected): | |||||
raise RuntimeError("This doesn't look like a version 1 cache directory") | |||||
config = dict() | |||||
else: | |||||
with open('config') as conf: | |||||
config = json.load(conf) | |||||
# Find any keys that aren't currently set but should be | |||||
for key in config_entries: | |||||
if key not in config: | |||||
if 'implicit' in config_entries[key]: | |||||
config[key] = config_entries[key]['implicit'] | |||||
else: | |||||
config[key] = config_entries[key]['default'] | |||||
if key not in args: | |||||
args[key] = config_entries[key]['default'] | |||||
#Now we go through each entry in args to see if it changes an existing config | |||||
#setting. | |||||
for key in args: | |||||
if args[key] != config[key]: | |||||
if 'converter' in config_entries[key]: | |||||
config_entries[key]['converter'](config[key], args[key]) | |||||
config[key] = args[key] | |||||
# and write the updated config file | |||||
with open('config', 'w') as conf: | |||||
json.dump(config, conf) |
@ -0,0 +1,878 @@ | |||||
""" | |||||
SCons.Builder | |||||
Builder object subsystem. | |||||
A Builder object is a callable that encapsulates information about how | |||||
to execute actions to create a target Node (file) from source Nodes | |||||
(files), and how to create those dependencies for tracking. | |||||
The main entry point here is the Builder() factory method. This provides | |||||
a procedural interface that creates the right underlying Builder object | |||||
based on the keyword arguments supplied and the types of the arguments. | |||||
The goal is for this external interface to be simple enough that the | |||||
vast majority of users can create new Builders as necessary to support | |||||
building new types of files in their configurations, without having to | |||||
dive any deeper into this subsystem. | |||||
The base class here is BuilderBase. This is a concrete base class which | |||||
does, in fact, represent the Builder objects that we (or users) create. | |||||
There is also a proxy that looks like a Builder: | |||||
CompositeBuilder | |||||
This proxies for a Builder with an action that is actually a | |||||
dictionary that knows how to map file suffixes to a specific | |||||
action. This is so that we can invoke different actions | |||||
(compilers, compile options) for different flavors of source | |||||
files. | |||||
Builders and their proxies have the following public interface methods | |||||
used by other modules: | |||||
- __call__() | |||||
THE public interface. Calling a Builder object (with the | |||||
use of internal helper methods) sets up the target and source | |||||
dependencies, appropriate mapping to a specific action, and the | |||||
environment manipulation necessary for overridden construction | |||||
variable. This also takes care of warning about possible mistakes | |||||
in keyword arguments. | |||||
- add_emitter() | |||||
Adds an emitter for a specific file suffix, used by some Tool | |||||
modules to specify that (for example) a yacc invocation on a .y | |||||
can create a .h *and* a .c file. | |||||
- add_action() | |||||
Adds an action for a specific file suffix, heavily used by | |||||
Tool modules to add their specific action(s) for turning | |||||
a source file into an object file to the global static | |||||
and shared object file Builders. | |||||
There are the following methods for internal use within this module: | |||||
- _execute() | |||||
The internal method that handles the heavily lifting when a | |||||
Builder is called. This is used so that the __call__() methods | |||||
can set up warning about possible mistakes in keyword-argument | |||||
overrides, and *then* execute all of the steps necessary so that | |||||
the warnings only occur once. | |||||
- get_name() | |||||
Returns the Builder's name within a specific Environment, | |||||
primarily used to try to return helpful information in error | |||||
messages. | |||||
- adjust_suffix() | |||||
- get_prefix() | |||||
- get_suffix() | |||||
- get_src_suffix() | |||||
- set_src_suffix() | |||||
Miscellaneous stuff for handling the prefix and suffix | |||||
manipulation we use in turning source file names into target | |||||
file names. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Builder.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import collections | |||||
import SCons.Action | |||||
import SCons.Debug | |||||
from SCons.Debug import logInstanceCreation | |||||
from SCons.Errors import InternalError, UserError | |||||
import SCons.Executor | |||||
import SCons.Memoize | |||||
import SCons.Util | |||||
import SCons.Warnings | |||||
class _Null(object): | |||||
pass | |||||
_null = _Null | |||||
def match_splitext(path, suffixes = []): | |||||
if suffixes: | |||||
matchsuf = [S for S in suffixes if path[-len(S):] == S] | |||||
if matchsuf: | |||||
suf = max([(len(_f),_f) for _f in matchsuf])[1] | |||||
return [path[:-len(suf)], path[-len(suf):]] | |||||
return SCons.Util.splitext(path) | |||||
class DictCmdGenerator(SCons.Util.Selector): | |||||
"""This is a callable class that can be used as a | |||||
command generator function. It holds on to a dictionary | |||||
mapping file suffixes to Actions. It uses that dictionary | |||||
to return the proper action based on the file suffix of | |||||
the source file.""" | |||||
def __init__(self, dict=None, source_ext_match=1): | |||||
SCons.Util.Selector.__init__(self, dict) | |||||
self.source_ext_match = source_ext_match | |||||
def src_suffixes(self): | |||||
return list(self.keys()) | |||||
def add_action(self, suffix, action): | |||||
"""Add a suffix-action pair to the mapping. | |||||
""" | |||||
self[suffix] = action | |||||
def __call__(self, target, source, env, for_signature): | |||||
if not source: | |||||
return [] | |||||
if self.source_ext_match: | |||||
suffixes = self.src_suffixes() | |||||
ext = None | |||||
for src in map(str, source): | |||||
my_ext = match_splitext(src, suffixes)[1] | |||||
if ext and my_ext != ext: | |||||
raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" | |||||
% (repr(list(map(str, target))), src, ext, my_ext)) | |||||
ext = my_ext | |||||
else: | |||||
ext = match_splitext(str(source[0]), self.src_suffixes())[1] | |||||
if not ext: | |||||
#return ext | |||||
raise UserError("While building `%s': " | |||||
"Cannot deduce file extension from source files: %s" | |||||
% (repr(list(map(str, target))), repr(list(map(str, source))))) | |||||
try: | |||||
ret = SCons.Util.Selector.__call__(self, env, source, ext) | |||||
except KeyError as e: | |||||
raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) | |||||
if ret is None: | |||||
raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ | |||||
(repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) | |||||
return ret | |||||
class CallableSelector(SCons.Util.Selector): | |||||
"""A callable dictionary that will, in turn, call the value it | |||||
finds if it can.""" | |||||
def __call__(self, env, source): | |||||
value = SCons.Util.Selector.__call__(self, env, source) | |||||
if callable(value): | |||||
value = value(env, source) | |||||
return value | |||||
class DictEmitter(SCons.Util.Selector): | |||||
"""A callable dictionary that maps file suffixes to emitters. | |||||
When called, it finds the right emitter in its dictionary for the | |||||
suffix of the first source file, and calls that emitter to get the | |||||
right lists of targets and sources to return. If there's no emitter | |||||
for the suffix in its dictionary, the original target and source are | |||||
returned. | |||||
""" | |||||
def __call__(self, target, source, env): | |||||
emitter = SCons.Util.Selector.__call__(self, env, source) | |||||
if emitter: | |||||
target, source = emitter(target, source, env) | |||||
return (target, source) | |||||
class ListEmitter(collections.UserList): | |||||
"""A callable list of emitters that calls each in sequence, | |||||
returning the result. | |||||
""" | |||||
def __call__(self, target, source, env): | |||||
for e in self.data: | |||||
target, source = e(target, source, env) | |||||
return (target, source) | |||||
# These are a common errors when calling a Builder; | |||||
# they are similar to the 'target' and 'source' keyword args to builders, | |||||
# so we issue warnings when we see them. The warnings can, of course, | |||||
# be disabled. | |||||
misleading_keywords = { | |||||
'targets' : 'target', | |||||
'sources' : 'source', | |||||
} | |||||
class OverrideWarner(collections.UserDict): | |||||
"""A class for warning about keyword arguments that we use as | |||||
overrides in a Builder call. | |||||
This class exists to handle the fact that a single Builder call | |||||
can actually invoke multiple builders. This class only emits the | |||||
warnings once, no matter how many Builders are invoked. | |||||
""" | |||||
def __init__(self, dict): | |||||
collections.UserDict.__init__(self, dict) | |||||
if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.OverrideWarner') | |||||
self.already_warned = None | |||||
def warn(self): | |||||
if self.already_warned: | |||||
return | |||||
for k in list(self.keys()): | |||||
if k in misleading_keywords: | |||||
alt = misleading_keywords[k] | |||||
msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) | |||||
SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) | |||||
self.already_warned = 1 | |||||
def Builder(**kw): | |||||
"""A factory for builder objects.""" | |||||
composite = None | |||||
if 'generator' in kw: | |||||
if 'action' in kw: | |||||
raise UserError("You must not specify both an action and a generator.") | |||||
kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) | |||||
del kw['generator'] | |||||
elif 'action' in kw: | |||||
source_ext_match = kw.get('source_ext_match', 1) | |||||
if 'source_ext_match' in kw: | |||||
del kw['source_ext_match'] | |||||
if SCons.Util.is_Dict(kw['action']): | |||||
composite = DictCmdGenerator(kw['action'], source_ext_match) | |||||
kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) | |||||
kw['src_suffix'] = composite.src_suffixes() | |||||
else: | |||||
kw['action'] = SCons.Action.Action(kw['action']) | |||||
if 'emitter' in kw: | |||||
emitter = kw['emitter'] | |||||
if SCons.Util.is_String(emitter): | |||||
# This allows users to pass in an Environment | |||||
# variable reference (like "$FOO") as an emitter. | |||||
# We will look in that Environment variable for | |||||
# a callable to use as the actual emitter. | |||||
var = SCons.Util.get_environment_var(emitter) | |||||
if not var: | |||||
raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) | |||||
kw['emitter'] = EmitterProxy(var) | |||||
elif SCons.Util.is_Dict(emitter): | |||||
kw['emitter'] = DictEmitter(emitter) | |||||
elif SCons.Util.is_List(emitter): | |||||
kw['emitter'] = ListEmitter(emitter) | |||||
result = BuilderBase(**kw) | |||||
if not composite is None: | |||||
result = CompositeBuilder(result, composite) | |||||
return result | |||||
def _node_errors(builder, env, tlist, slist): | |||||
"""Validate that the lists of target and source nodes are | |||||
legal for this builder and environment. Raise errors or | |||||
issue warnings as appropriate. | |||||
""" | |||||
# First, figure out if there are any errors in the way the targets | |||||
# were specified. | |||||
for t in tlist: | |||||
if t.side_effect: | |||||
raise UserError("Multiple ways to build the same target were specified for: %s" % t) | |||||
if t.has_explicit_builder(): | |||||
if not t.env is None and not t.env is env: | |||||
action = t.builder.action | |||||
t_contents = t.builder.action.get_contents(tlist, slist, t.env) | |||||
contents = builder.action.get_contents(tlist, slist, env) | |||||
if t_contents == contents: | |||||
msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) | |||||
SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) | |||||
else: | |||||
msg = "Two environments with different actions were specified for the same target: %s\n(action 1: %s)\n(action 2: %s)" % (t,t_contents.decode('utf-8'),contents.decode('utf-8')) | |||||
raise UserError(msg) | |||||
if builder.multi: | |||||
if t.builder != builder: | |||||
msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) | |||||
raise UserError(msg) | |||||
# TODO(batch): list constructed each time! | |||||
if t.get_executor().get_all_targets() != tlist: | |||||
msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist))) | |||||
raise UserError(msg) | |||||
elif t.sources != slist: | |||||
msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist))) | |||||
raise UserError(msg) | |||||
if builder.single_source: | |||||
if len(slist) > 1: | |||||
raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) | |||||
class EmitterProxy(object): | |||||
"""This is a callable class that can act as a | |||||
Builder emitter. It holds on to a string that | |||||
is a key into an Environment dictionary, and will | |||||
look there at actual build time to see if it holds | |||||
a callable. If so, we will call that as the actual | |||||
emitter.""" | |||||
def __init__(self, var): | |||||
self.var = SCons.Util.to_String(var) | |||||
def __call__(self, target, source, env): | |||||
emitter = self.var | |||||
# Recursively substitute the variable. | |||||
# We can't use env.subst() because it deals only | |||||
# in strings. Maybe we should change that? | |||||
while SCons.Util.is_String(emitter) and emitter in env: | |||||
emitter = env[emitter] | |||||
if callable(emitter): | |||||
target, source = emitter(target, source, env) | |||||
elif SCons.Util.is_List(emitter): | |||||
for e in emitter: | |||||
target, source = e(target, source, env) | |||||
return (target, source) | |||||
def __eq__(self, other): | |||||
return self.var == other.var | |||||
def __lt__(self, other): | |||||
return self.var < other.var | |||||
class BuilderBase(object): | |||||
"""Base class for Builders, objects that create output | |||||
nodes (files) from input nodes (files). | |||||
""" | |||||
def __init__(self, action = None, | |||||
prefix = '', | |||||
suffix = '', | |||||
src_suffix = '', | |||||
target_factory = None, | |||||
source_factory = None, | |||||
target_scanner = None, | |||||
source_scanner = None, | |||||
emitter = None, | |||||
multi = 0, | |||||
env = None, | |||||
single_source = 0, | |||||
name = None, | |||||
chdir = _null, | |||||
is_explicit = 1, | |||||
src_builder = None, | |||||
ensure_suffix = False, | |||||
**overrides): | |||||
if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.BuilderBase') | |||||
self._memo = {} | |||||
self.action = action | |||||
self.multi = multi | |||||
if SCons.Util.is_Dict(prefix): | |||||
prefix = CallableSelector(prefix) | |||||
self.prefix = prefix | |||||
if SCons.Util.is_Dict(suffix): | |||||
suffix = CallableSelector(suffix) | |||||
self.env = env | |||||
self.single_source = single_source | |||||
if 'overrides' in overrides: | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, | |||||
"The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ | |||||
"\tspecify the items as keyword arguments to the Builder() call instead.") | |||||
overrides.update(overrides['overrides']) | |||||
del overrides['overrides'] | |||||
if 'scanner' in overrides: | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, | |||||
"The \"scanner\" keyword to Builder() creation has been deprecated;\n" | |||||
"\tuse: source_scanner or target_scanner as appropriate.") | |||||
del overrides['scanner'] | |||||
self.overrides = overrides | |||||
self.set_suffix(suffix) | |||||
self.set_src_suffix(src_suffix) | |||||
self.ensure_suffix = ensure_suffix | |||||
self.target_factory = target_factory | |||||
self.source_factory = source_factory | |||||
self.target_scanner = target_scanner | |||||
self.source_scanner = source_scanner | |||||
self.emitter = emitter | |||||
# Optional Builder name should only be used for Builders | |||||
# that don't get attached to construction environments. | |||||
if name: | |||||
self.name = name | |||||
self.executor_kw = {} | |||||
if not chdir is _null: | |||||
self.executor_kw['chdir'] = chdir | |||||
self.is_explicit = is_explicit | |||||
if src_builder is None: | |||||
src_builder = [] | |||||
elif not SCons.Util.is_List(src_builder): | |||||
src_builder = [ src_builder ] | |||||
self.src_builder = src_builder | |||||
def __nonzero__(self): | |||||
raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead") | |||||
def __bool__(self): | |||||
return self.__nonzero__() | |||||
def get_name(self, env): | |||||
"""Attempts to get the name of the Builder. | |||||
Look at the BUILDERS variable of env, expecting it to be a | |||||
dictionary containing this Builder, and return the key of the | |||||
dictionary. If there's no key, then return a directly-configured | |||||
name (if there is one) or the name of the class (by default).""" | |||||
try: | |||||
index = list(env['BUILDERS'].values()).index(self) | |||||
return list(env['BUILDERS'].keys())[index] | |||||
except (AttributeError, KeyError, TypeError, ValueError): | |||||
try: | |||||
return self.name | |||||
except AttributeError: | |||||
return str(self.__class__) | |||||
def __eq__(self, other): | |||||
return self.__dict__ == other.__dict__ | |||||
def splitext(self, path, env=None): | |||||
if not env: | |||||
env = self.env | |||||
if env: | |||||
suffixes = self.src_suffixes(env) | |||||
else: | |||||
suffixes = [] | |||||
return match_splitext(path, suffixes) | |||||
def _adjustixes(self, files, pre, suf, ensure_suffix=False): | |||||
if not files: | |||||
return [] | |||||
result = [] | |||||
if not SCons.Util.is_List(files): | |||||
files = [files] | |||||
for f in files: | |||||
if SCons.Util.is_String(f): | |||||
f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) | |||||
result.append(f) | |||||
return result | |||||
def _create_nodes(self, env, target = None, source = None): | |||||
"""Create and return lists of target and source nodes. | |||||
""" | |||||
src_suf = self.get_src_suffix(env) | |||||
target_factory = env.get_factory(self.target_factory) | |||||
source_factory = env.get_factory(self.source_factory) | |||||
source = self._adjustixes(source, None, src_suf) | |||||
slist = env.arg2nodes(source, source_factory) | |||||
pre = self.get_prefix(env, slist) | |||||
suf = self.get_suffix(env, slist) | |||||
if target is None: | |||||
try: | |||||
t_from_s = slist[0].target_from_source | |||||
except AttributeError: | |||||
raise UserError("Do not know how to create a target from source `%s'" % slist[0]) | |||||
except IndexError: | |||||
tlist = [] | |||||
else: | |||||
splitext = lambda S: self.splitext(S,env) | |||||
tlist = [ t_from_s(pre, suf, splitext) ] | |||||
else: | |||||
target = self._adjustixes(target, pre, suf, self.ensure_suffix) | |||||
tlist = env.arg2nodes(target, target_factory, target=target, source=source) | |||||
if self.emitter: | |||||
# The emitter is going to do str(node), but because we're | |||||
# being called *from* a builder invocation, the new targets | |||||
# don't yet have a builder set on them and will look like | |||||
# source files. Fool the emitter's str() calls by setting | |||||
# up a temporary builder on the new targets. | |||||
new_targets = [] | |||||
for t in tlist: | |||||
if not t.is_derived(): | |||||
t.builder_set(self) | |||||
new_targets.append(t) | |||||
orig_tlist = tlist[:] | |||||
orig_slist = slist[:] | |||||
target, source = self.emitter(target=tlist, source=slist, env=env) | |||||
# Now delete the temporary builders that we attached to any | |||||
# new targets, so that _node_errors() doesn't do weird stuff | |||||
# to them because it thinks they already have builders. | |||||
for t in new_targets: | |||||
if t.builder is self: | |||||
# Only delete the temporary builder if the emitter | |||||
# didn't change it on us. | |||||
t.builder_set(None) | |||||
# Have to call arg2nodes yet again, since it is legal for | |||||
# emitters to spit out strings as well as Node instances. | |||||
tlist = env.arg2nodes(target, target_factory, | |||||
target=orig_tlist, source=orig_slist) | |||||
slist = env.arg2nodes(source, source_factory, | |||||
target=orig_tlist, source=orig_slist) | |||||
return tlist, slist | |||||
def _execute(self, env, target, source, overwarn={}, executor_kw={}): | |||||
# We now assume that target and source are lists or None. | |||||
if self.src_builder: | |||||
source = self.src_builder_sources(env, source, overwarn) | |||||
if self.single_source and len(source) > 1 and target is None: | |||||
result = [] | |||||
if target is None: target = [None]*len(source) | |||||
for tgt, src in zip(target, source): | |||||
if not tgt is None: tgt = [tgt] | |||||
if not src is None: src = [src] | |||||
result.extend(self._execute(env, tgt, src, overwarn)) | |||||
return SCons.Node.NodeList(result) | |||||
overwarn.warn() | |||||
tlist, slist = self._create_nodes(env, target, source) | |||||
# Check for errors with the specified target/source lists. | |||||
_node_errors(self, env, tlist, slist) | |||||
# The targets are fine, so find or make the appropriate Executor to | |||||
# build this particular list of targets from this particular list of | |||||
# sources. | |||||
executor = None | |||||
key = None | |||||
if self.multi: | |||||
try: | |||||
executor = tlist[0].get_executor(create = 0) | |||||
except (AttributeError, IndexError): | |||||
pass | |||||
else: | |||||
executor.add_sources(slist) | |||||
if executor is None: | |||||
if not self.action: | |||||
fmt = "Builder %s must have an action to build %s." | |||||
raise UserError(fmt % (self.get_name(env or self.env), | |||||
list(map(str,tlist)))) | |||||
key = self.action.batch_key(env or self.env, tlist, slist) | |||||
if key: | |||||
try: | |||||
executor = SCons.Executor.GetBatchExecutor(key) | |||||
except KeyError: | |||||
pass | |||||
else: | |||||
executor.add_batch(tlist, slist) | |||||
if executor is None: | |||||
executor = SCons.Executor.Executor(self.action, env, [], | |||||
tlist, slist, executor_kw) | |||||
if key: | |||||
SCons.Executor.AddBatchExecutor(key, executor) | |||||
# Now set up the relevant information in the target Nodes themselves. | |||||
for t in tlist: | |||||
t.cwd = env.fs.getcwd() | |||||
t.builder_set(self) | |||||
t.env_set(env) | |||||
t.add_source(slist) | |||||
t.set_executor(executor) | |||||
t.set_explicit(self.is_explicit) | |||||
return SCons.Node.NodeList(tlist) | |||||
def __call__(self, env, target=None, source=None, chdir=_null, **kw): | |||||
# We now assume that target and source are lists or None. | |||||
# The caller (typically Environment.BuilderWrapper) is | |||||
# responsible for converting any scalar values to lists. | |||||
if chdir is _null: | |||||
ekw = self.executor_kw | |||||
else: | |||||
ekw = self.executor_kw.copy() | |||||
ekw['chdir'] = chdir | |||||
if 'chdir' in ekw and SCons.Util.is_String(ekw['chdir']): | |||||
ekw['chdir'] = env.subst(ekw['chdir']) | |||||
if kw: | |||||
if 'srcdir' in kw: | |||||
def prependDirIfRelative(f, srcdir=kw['srcdir']): | |||||
import os.path | |||||
if SCons.Util.is_String(f) and not os.path.isabs(f): | |||||
f = os.path.join(srcdir, f) | |||||
return f | |||||
if not SCons.Util.is_List(source): | |||||
source = [source] | |||||
source = list(map(prependDirIfRelative, source)) | |||||
del kw['srcdir'] | |||||
if self.overrides: | |||||
env_kw = self.overrides.copy() | |||||
env_kw.update(kw) | |||||
else: | |||||
env_kw = kw | |||||
else: | |||||
env_kw = self.overrides | |||||
env = env.Override(env_kw) | |||||
return self._execute(env, target, source, OverrideWarner(kw), ekw) | |||||
def adjust_suffix(self, suff): | |||||
if suff and not suff[0] in [ '.', '_', '$' ]: | |||||
return '.' + suff | |||||
return suff | |||||
def get_prefix(self, env, sources=[]): | |||||
prefix = self.prefix | |||||
if callable(prefix): | |||||
prefix = prefix(env, sources) | |||||
return env.subst(prefix) | |||||
def set_suffix(self, suffix): | |||||
if not callable(suffix): | |||||
suffix = self.adjust_suffix(suffix) | |||||
self.suffix = suffix | |||||
def get_suffix(self, env, sources=[]): | |||||
suffix = self.suffix | |||||
if callable(suffix): | |||||
suffix = suffix(env, sources) | |||||
return env.subst(suffix) | |||||
def set_src_suffix(self, src_suffix): | |||||
if not src_suffix: | |||||
src_suffix = [] | |||||
elif not SCons.Util.is_List(src_suffix): | |||||
src_suffix = [ src_suffix ] | |||||
self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix] | |||||
def get_src_suffix(self, env): | |||||
"""Get the first src_suffix in the list of src_suffixes.""" | |||||
ret = self.src_suffixes(env) | |||||
if not ret: | |||||
return '' | |||||
return ret[0] | |||||
def add_emitter(self, suffix, emitter): | |||||
"""Add a suffix-emitter mapping to this Builder. | |||||
This assumes that emitter has been initialized with an | |||||
appropriate dictionary type, and will throw a TypeError if | |||||
not, so the caller is responsible for knowing that this is an | |||||
appropriate method to call for the Builder in question. | |||||
""" | |||||
self.emitter[suffix] = emitter | |||||
def add_src_builder(self, builder): | |||||
""" | |||||
Add a new Builder to the list of src_builders. | |||||
This requires wiping out cached values so that the computed | |||||
lists of source suffixes get re-calculated. | |||||
""" | |||||
self._memo = {} | |||||
self.src_builder.append(builder) | |||||
def _get_sdict(self, env): | |||||
""" | |||||
Returns a dictionary mapping all of the source suffixes of all | |||||
src_builders of this Builder to the underlying Builder that | |||||
should be called first. | |||||
This dictionary is used for each target specified, so we save a | |||||
lot of extra computation by memoizing it for each construction | |||||
environment. | |||||
Note that this is re-computed each time, not cached, because there | |||||
might be changes to one of our source Builders (or one of their | |||||
source Builders, and so on, and so on...) that we can't "see." | |||||
The underlying methods we call cache their computed values, | |||||
though, so we hope repeatedly aggregating them into a dictionary | |||||
like this won't be too big a hit. We may need to look for a | |||||
better way to do this if performance data show this has turned | |||||
into a significant bottleneck. | |||||
""" | |||||
sdict = {} | |||||
for bld in self.get_src_builders(env): | |||||
for suf in bld.src_suffixes(env): | |||||
sdict[suf] = bld | |||||
return sdict | |||||
def src_builder_sources(self, env, source, overwarn={}): | |||||
sdict = self._get_sdict(env) | |||||
src_suffixes = self.src_suffixes(env) | |||||
lengths = list(set(map(len, src_suffixes))) | |||||
def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): | |||||
node_suffixes = [name[-l:] for l in lengths] | |||||
for suf in src_suffixes: | |||||
if suf in node_suffixes: | |||||
return suf | |||||
return None | |||||
result = [] | |||||
for s in SCons.Util.flatten(source): | |||||
if SCons.Util.is_String(s): | |||||
match_suffix = match_src_suffix(env.subst(s)) | |||||
if not match_suffix and not '.' in s: | |||||
src_suf = self.get_src_suffix(env) | |||||
s = self._adjustixes(s, None, src_suf)[0] | |||||
else: | |||||
match_suffix = match_src_suffix(s.name) | |||||
if match_suffix: | |||||
try: | |||||
bld = sdict[match_suffix] | |||||
except KeyError: | |||||
result.append(s) | |||||
else: | |||||
tlist = bld._execute(env, None, [s], overwarn) | |||||
# If the subsidiary Builder returned more than one | |||||
# target, then filter out any sources that this | |||||
# Builder isn't capable of building. | |||||
if len(tlist) > 1: | |||||
tlist = [t for t in tlist if match_src_suffix(t.name)] | |||||
result.extend(tlist) | |||||
else: | |||||
result.append(s) | |||||
source_factory = env.get_factory(self.source_factory) | |||||
return env.arg2nodes(result, source_factory) | |||||
def _get_src_builders_key(self, env): | |||||
return id(env) | |||||
@SCons.Memoize.CountDictCall(_get_src_builders_key) | |||||
def get_src_builders(self, env): | |||||
""" | |||||
Returns the list of source Builders for this Builder. | |||||
This exists mainly to look up Builders referenced as | |||||
strings in the 'BUILDER' variable of the construction | |||||
environment and cache the result. | |||||
""" | |||||
memo_key = id(env) | |||||
try: | |||||
memo_dict = self._memo['get_src_builders'] | |||||
except KeyError: | |||||
memo_dict = {} | |||||
self._memo['get_src_builders'] = memo_dict | |||||
else: | |||||
try: | |||||
return memo_dict[memo_key] | |||||
except KeyError: | |||||
pass | |||||
builders = [] | |||||
for bld in self.src_builder: | |||||
if SCons.Util.is_String(bld): | |||||
try: | |||||
bld = env['BUILDERS'][bld] | |||||
except KeyError: | |||||
continue | |||||
builders.append(bld) | |||||
memo_dict[memo_key] = builders | |||||
return builders | |||||
def _subst_src_suffixes_key(self, env): | |||||
return id(env) | |||||
@SCons.Memoize.CountDictCall(_subst_src_suffixes_key) | |||||
def subst_src_suffixes(self, env): | |||||
""" | |||||
The suffix list may contain construction variable expansions, | |||||
so we have to evaluate the individual strings. To avoid doing | |||||
this over and over, we memoize the results for each construction | |||||
environment. | |||||
""" | |||||
memo_key = id(env) | |||||
try: | |||||
memo_dict = self._memo['subst_src_suffixes'] | |||||
except KeyError: | |||||
memo_dict = {} | |||||
self._memo['subst_src_suffixes'] = memo_dict | |||||
else: | |||||
try: | |||||
return memo_dict[memo_key] | |||||
except KeyError: | |||||
pass | |||||
suffixes = [env.subst(x) for x in self.src_suffix] | |||||
memo_dict[memo_key] = suffixes | |||||
return suffixes | |||||
def src_suffixes(self, env): | |||||
""" | |||||
Returns the list of source suffixes for all src_builders of this | |||||
Builder. | |||||
This is essentially a recursive descent of the src_builder "tree." | |||||
(This value isn't cached because there may be changes in a | |||||
src_builder many levels deep that we can't see.) | |||||
""" | |||||
sdict = {} | |||||
suffixes = self.subst_src_suffixes(env) | |||||
for s in suffixes: | |||||
sdict[s] = 1 | |||||
for builder in self.get_src_builders(env): | |||||
for s in builder.src_suffixes(env): | |||||
if s not in sdict: | |||||
sdict[s] = 1 | |||||
suffixes.append(s) | |||||
return suffixes | |||||
class CompositeBuilder(SCons.Util.Proxy): | |||||
"""A Builder Proxy whose main purpose is to always have | |||||
a DictCmdGenerator as its action, and to provide access | |||||
to the DictCmdGenerator's add_action() method. | |||||
""" | |||||
def __init__(self, builder, cmdgen): | |||||
if SCons.Debug.track_instances: logInstanceCreation(self, 'Builder.CompositeBuilder') | |||||
SCons.Util.Proxy.__init__(self, builder) | |||||
# cmdgen should always be an instance of DictCmdGenerator. | |||||
self.cmdgen = cmdgen | |||||
self.builder = builder | |||||
__call__ = SCons.Util.Delegate('__call__') | |||||
def add_action(self, suffix, action): | |||||
self.cmdgen.add_action(suffix, action) | |||||
self.set_src_suffix(self.cmdgen.src_suffixes()) | |||||
def is_a_Builder(obj): | |||||
""""Returns True if the specified obj is one of our Builder classes. | |||||
The test is complicated a bit by the fact that CompositeBuilder | |||||
is a proxy, not a subclass of BuilderBase. | |||||
""" | |||||
return (isinstance(obj, BuilderBase) | |||||
or isinstance(obj, CompositeBuilder) | |||||
or callable(obj)) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,281 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/CacheDir.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """ | |||||
CacheDir support | |||||
""" | |||||
import json | |||||
import os | |||||
import stat | |||||
import sys | |||||
import SCons.Action | |||||
import SCons.Warnings | |||||
cache_enabled = True | |||||
cache_debug = False | |||||
cache_force = False | |||||
cache_show = False | |||||
cache_readonly = False | |||||
def CacheRetrieveFunc(target, source, env): | |||||
t = target[0] | |||||
fs = t.fs | |||||
cd = env.get_CacheDir() | |||||
cachedir, cachefile = cd.cachepath(t) | |||||
if not fs.exists(cachefile): | |||||
cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) | |||||
return 1 | |||||
cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) | |||||
if SCons.Action.execute_actions: | |||||
if fs.islink(cachefile): | |||||
fs.symlink(fs.readlink(cachefile), t.get_internal_path()) | |||||
else: | |||||
env.copy_from_cache(cachefile, t.get_internal_path()) | |||||
st = fs.stat(cachefile) | |||||
fs.chmod(t.get_internal_path(), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) | |||||
return 0 | |||||
def CacheRetrieveString(target, source, env): | |||||
t = target[0] | |||||
fs = t.fs | |||||
cd = env.get_CacheDir() | |||||
cachedir, cachefile = cd.cachepath(t) | |||||
if t.fs.exists(cachefile): | |||||
return "Retrieved `%s' from cache" % t.get_internal_path() | |||||
return None | |||||
CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) | |||||
CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) | |||||
def CachePushFunc(target, source, env): | |||||
if cache_readonly: | |||||
return | |||||
t = target[0] | |||||
if t.nocache: | |||||
return | |||||
fs = t.fs | |||||
cd = env.get_CacheDir() | |||||
cachedir, cachefile = cd.cachepath(t) | |||||
if fs.exists(cachefile): | |||||
# Don't bother copying it if it's already there. Note that | |||||
# usually this "shouldn't happen" because if the file already | |||||
# existed in cache, we'd have retrieved the file from there, | |||||
# not built it. This can happen, though, in a race, if some | |||||
# other person running the same build pushes their copy to | |||||
# the cache after we decide we need to build it but before our | |||||
# build completes. | |||||
cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) | |||||
return | |||||
cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) | |||||
tempfile = cachefile+'.tmp'+str(os.getpid()) | |||||
errfmt = "Unable to copy %s to cache. Cache file is %s" | |||||
if not fs.isdir(cachedir): | |||||
try: | |||||
fs.makedirs(cachedir) | |||||
except EnvironmentError: | |||||
# We may have received an exception because another process | |||||
# has beaten us creating the directory. | |||||
if not fs.isdir(cachedir): | |||||
msg = errfmt % (str(target), cachefile) | |||||
raise SCons.Errors.EnvironmentError(msg) | |||||
try: | |||||
if fs.islink(t.get_internal_path()): | |||||
fs.symlink(fs.readlink(t.get_internal_path()), tempfile) | |||||
else: | |||||
fs.copy2(t.get_internal_path(), tempfile) | |||||
fs.rename(tempfile, cachefile) | |||||
st = fs.stat(t.get_internal_path()) | |||||
fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) | |||||
except EnvironmentError: | |||||
# It's possible someone else tried writing the file at the | |||||
# same time we did, or else that there was some problem like | |||||
# the CacheDir being on a separate file system that's full. | |||||
# In any case, inability to push a file to cache doesn't affect | |||||
# the correctness of the build, so just print a warning. | |||||
msg = errfmt % (str(target), cachefile) | |||||
SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg) | |||||
CachePush = SCons.Action.Action(CachePushFunc, None) | |||||
# Nasty hack to cut down to one warning for each cachedir path that needs | |||||
# upgrading. | |||||
warned = dict() | |||||
class CacheDir(object): | |||||
def __init__(self, path): | |||||
try: | |||||
import hashlib | |||||
except ImportError: | |||||
msg = "No hashlib or MD5 module available, CacheDir() not supported" | |||||
SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) | |||||
path = None | |||||
self.path = path | |||||
self.current_cache_debug = None | |||||
self.debugFP = None | |||||
self.config = dict() | |||||
if path is None: | |||||
return | |||||
# See if there's a config file in the cache directory. If there is, | |||||
# use it. If there isn't, and the directory exists and isn't empty, | |||||
# produce a warning. If the directory doesn't exist or is empty, | |||||
# write a config file. | |||||
config_file = os.path.join(path, 'config') | |||||
if not os.path.exists(config_file): | |||||
# A note: There is a race hazard here, if two processes start and | |||||
# attempt to create the cache directory at the same time. However, | |||||
# python doesn't really give you the option to do exclusive file | |||||
# creation (it doesn't even give you the option to error on opening | |||||
# an existing file for writing...). The ordering of events here | |||||
# as an attempt to alleviate this, on the basis that it's a pretty | |||||
# unlikely occurence (it'd require two builds with a brand new cache | |||||
# directory) | |||||
if os.path.isdir(path) and len(os.listdir(path)) != 0: | |||||
self.config['prefix_len'] = 1 | |||||
# When building the project I was testing this on, the warning | |||||
# was output over 20 times. That seems excessive | |||||
global warned | |||||
if self.path not in warned: | |||||
msg = "Please upgrade your cache by running " +\ | |||||
" scons-configure-cache.py " + self.path | |||||
SCons.Warnings.warn(SCons.Warnings.CacheVersionWarning, msg) | |||||
warned[self.path] = True | |||||
else: | |||||
if not os.path.isdir(path): | |||||
try: | |||||
os.makedirs(path) | |||||
except OSError: | |||||
# If someone else is trying to create the directory at | |||||
# the same time as me, bad things will happen | |||||
msg = "Failed to create cache directory " + path | |||||
raise SCons.Errors.EnvironmentError(msg) | |||||
self.config['prefix_len'] = 2 | |||||
if not os.path.exists(config_file): | |||||
try: | |||||
with open(config_file, 'w') as config: | |||||
json.dump(self.config, config) | |||||
except: | |||||
msg = "Failed to write cache configuration for " + path | |||||
raise SCons.Errors.EnvironmentError(msg) | |||||
else: | |||||
try: | |||||
with open(config_file) as config: | |||||
self.config = json.load(config) | |||||
except ValueError: | |||||
msg = "Failed to read cache configuration for " + path | |||||
raise SCons.Errors.EnvironmentError(msg) | |||||
def CacheDebug(self, fmt, target, cachefile): | |||||
if cache_debug != self.current_cache_debug: | |||||
if cache_debug == '-': | |||||
self.debugFP = sys.stdout | |||||
elif cache_debug: | |||||
self.debugFP = open(cache_debug, 'w') | |||||
else: | |||||
self.debugFP = None | |||||
self.current_cache_debug = cache_debug | |||||
if self.debugFP: | |||||
self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) | |||||
def is_enabled(self): | |||||
return cache_enabled and not self.path is None | |||||
def is_readonly(self): | |||||
return cache_readonly | |||||
def cachepath(self, node): | |||||
""" | |||||
""" | |||||
if not self.is_enabled(): | |||||
return None, None | |||||
sig = node.get_cachedir_bsig() | |||||
subdir = sig[:self.config['prefix_len']].upper() | |||||
dir = os.path.join(self.path, subdir) | |||||
return dir, os.path.join(dir, sig) | |||||
def retrieve(self, node): | |||||
""" | |||||
This method is called from multiple threads in a parallel build, | |||||
so only do thread safe stuff here. Do thread unsafe stuff in | |||||
built(). | |||||
Note that there's a special trick here with the execute flag | |||||
(one that's not normally done for other actions). Basically | |||||
if the user requested a no_exec (-n) build, then | |||||
SCons.Action.execute_actions is set to 0 and when any action | |||||
is called, it does its showing but then just returns zero | |||||
instead of actually calling the action execution operation. | |||||
The problem for caching is that if the file does NOT exist in | |||||
cache then the CacheRetrieveString won't return anything to | |||||
show for the task, but the Action.__call__ won't call | |||||
CacheRetrieveFunc; instead it just returns zero, which makes | |||||
the code below think that the file *was* successfully | |||||
retrieved from the cache, therefore it doesn't do any | |||||
subsequent building. However, the CacheRetrieveString didn't | |||||
print anything because it didn't actually exist in the cache, | |||||
and no more build actions will be performed, so the user just | |||||
sees nothing. The fix is to tell Action.__call__ to always | |||||
execute the CacheRetrieveFunc and then have the latter | |||||
explicitly check SCons.Action.execute_actions itself. | |||||
""" | |||||
if not self.is_enabled(): | |||||
return False | |||||
env = node.get_build_env() | |||||
if cache_show: | |||||
if CacheRetrieveSilent(node, [], env, execute=1) == 0: | |||||
node.build(presub=0, execute=0) | |||||
return True | |||||
else: | |||||
if CacheRetrieve(node, [], env, execute=1) == 0: | |||||
return True | |||||
return False | |||||
def push(self, node): | |||||
if self.is_readonly() or not self.is_enabled(): | |||||
return | |||||
return CachePush(node, [], node.get_build_env()) | |||||
def push_if_forced(self, node): | |||||
if cache_force: | |||||
return self.push(node) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,812 @@ | |||||
"""SCons.Conftest | |||||
Autoconf-like configuration support; low level implementation of tests. | |||||
""" | |||||
# | |||||
# Copyright (c) 2003 Stichting NLnet Labs | |||||
# Copyright (c) 2001, 2002, 2003 Steven Knight | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
# | |||||
# The purpose of this module is to define how a check is to be performed. | |||||
# Use one of the Check...() functions below. | |||||
# | |||||
# | |||||
# A context class is used that defines functions for carrying out the tests, | |||||
# logging and messages. The following methods and members must be present: | |||||
# | |||||
# context.Display(msg) Function called to print messages that are normally | |||||
# displayed for the user. Newlines are explicitly used. | |||||
# The text should also be written to the logfile! | |||||
# | |||||
# context.Log(msg) Function called to write to a log file. | |||||
# | |||||
# context.BuildProg(text, ext) | |||||
# Function called to build a program, using "ext" for the | |||||
# file extention. Must return an empty string for | |||||
# success, an error message for failure. | |||||
# For reliable test results building should be done just | |||||
# like an actual program would be build, using the same | |||||
# command and arguments (including configure results so | |||||
# far). | |||||
# | |||||
# context.CompileProg(text, ext) | |||||
# Function called to compile a program, using "ext" for | |||||
# the file extention. Must return an empty string for | |||||
# success, an error message for failure. | |||||
# For reliable test results compiling should be done just | |||||
# like an actual source file would be compiled, using the | |||||
# same command and arguments (including configure results | |||||
# so far). | |||||
# | |||||
# context.AppendLIBS(lib_name_list) | |||||
# Append "lib_name_list" to the value of LIBS. | |||||
# "lib_namelist" is a list of strings. | |||||
# Return the value of LIBS before changing it (any type | |||||
# can be used, it is passed to SetLIBS() later.) | |||||
# | |||||
# context.PrependLIBS(lib_name_list) | |||||
# Prepend "lib_name_list" to the value of LIBS. | |||||
# "lib_namelist" is a list of strings. | |||||
# Return the value of LIBS before changing it (any type | |||||
# can be used, it is passed to SetLIBS() later.) | |||||
# | |||||
# context.SetLIBS(value) | |||||
# Set LIBS to "value". The type of "value" is what | |||||
# AppendLIBS() returned. | |||||
# Return the value of LIBS before changing it (any type | |||||
# can be used, it is passed to SetLIBS() later.) | |||||
# | |||||
# context.headerfilename | |||||
# Name of file to append configure results to, usually | |||||
# "confdefs.h". | |||||
# The file must not exist or be empty when starting. | |||||
# Empty or None to skip this (some tests will not work!). | |||||
# | |||||
# context.config_h (may be missing). If present, must be a string, which | |||||
# will be filled with the contents of a config_h file. | |||||
# | |||||
# context.vardict Dictionary holding variables used for the tests and | |||||
# stores results from the tests, used for the build | |||||
# commands. | |||||
# Normally contains "CC", "LIBS", "CPPFLAGS", etc. | |||||
# | |||||
# context.havedict Dictionary holding results from the tests that are to | |||||
# be used inside a program. | |||||
# Names often start with "HAVE_". These are zero | |||||
# (feature not present) or one (feature present). Other | |||||
# variables may have any value, e.g., "PERLVERSION" can | |||||
# be a number and "SYSTEMNAME" a string. | |||||
# | |||||
import re | |||||
# | |||||
# PUBLIC VARIABLES | |||||
# | |||||
LogInputFiles = 1 # Set that to log the input files in case of a failed test | |||||
LogErrorMessages = 1 # Set that to log Conftest-generated error messages | |||||
# | |||||
# PUBLIC FUNCTIONS | |||||
# | |||||
# Generic remarks: | |||||
# - When a language is specified which is not supported the test fails. The | |||||
# message is a bit different, because not all the arguments for the normal | |||||
# message are available yet (chicken-egg problem). | |||||
def CheckBuilder(context, text = None, language = None): | |||||
""" | |||||
Configure check to see if the compiler works. | |||||
Note that this uses the current value of compiler and linker flags, make | |||||
sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. | |||||
"language" should be "C" or "C++" and is used to select the compiler. | |||||
Default is "C". | |||||
"text" may be used to specify the code to be build. | |||||
Returns an empty string for success, an error message for failure. | |||||
""" | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
context.Display("%s\n" % msg) | |||||
return msg | |||||
if not text: | |||||
text = """ | |||||
int main() { | |||||
return 0; | |||||
} | |||||
""" | |||||
context.Display("Checking if building a %s file works... " % lang) | |||||
ret = context.BuildProg(text, suffix) | |||||
_YesNoResult(context, ret, None, text) | |||||
return ret | |||||
def CheckCC(context): | |||||
""" | |||||
Configure check for a working C compiler. | |||||
This checks whether the C compiler, as defined in the $CC construction | |||||
variable, can compile a C source file. It uses the current $CCCOM value | |||||
too, so that it can test against non working flags. | |||||
""" | |||||
context.Display("Checking whether the C compiler works... ") | |||||
text = """ | |||||
int main() | |||||
{ | |||||
return 0; | |||||
} | |||||
""" | |||||
ret = _check_empty_program(context, 'CC', text, 'C') | |||||
_YesNoResult(context, ret, None, text) | |||||
return ret | |||||
def CheckSHCC(context): | |||||
""" | |||||
Configure check for a working shared C compiler. | |||||
This checks whether the C compiler, as defined in the $SHCC construction | |||||
variable, can compile a C source file. It uses the current $SHCCCOM value | |||||
too, so that it can test against non working flags. | |||||
""" | |||||
context.Display("Checking whether the (shared) C compiler works... ") | |||||
text = """ | |||||
int foo() | |||||
{ | |||||
return 0; | |||||
} | |||||
""" | |||||
ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True) | |||||
_YesNoResult(context, ret, None, text) | |||||
return ret | |||||
def CheckCXX(context): | |||||
""" | |||||
Configure check for a working CXX compiler. | |||||
This checks whether the CXX compiler, as defined in the $CXX construction | |||||
variable, can compile a CXX source file. It uses the current $CXXCOM value | |||||
too, so that it can test against non working flags. | |||||
""" | |||||
context.Display("Checking whether the C++ compiler works... ") | |||||
text = """ | |||||
int main() | |||||
{ | |||||
return 0; | |||||
} | |||||
""" | |||||
ret = _check_empty_program(context, 'CXX', text, 'C++') | |||||
_YesNoResult(context, ret, None, text) | |||||
return ret | |||||
def CheckSHCXX(context): | |||||
""" | |||||
Configure check for a working shared CXX compiler. | |||||
This checks whether the CXX compiler, as defined in the $SHCXX construction | |||||
variable, can compile a CXX source file. It uses the current $SHCXXCOM value | |||||
too, so that it can test against non working flags. | |||||
""" | |||||
context.Display("Checking whether the (shared) C++ compiler works... ") | |||||
text = """ | |||||
int main() | |||||
{ | |||||
return 0; | |||||
} | |||||
""" | |||||
ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True) | |||||
_YesNoResult(context, ret, None, text) | |||||
return ret | |||||
def _check_empty_program(context, comp, text, language, use_shared = False): | |||||
"""Return 0 on success, 1 otherwise.""" | |||||
if comp not in context.env or not context.env[comp]: | |||||
# The compiler construction variable is not set or empty | |||||
return 1 | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
return 1 | |||||
if use_shared: | |||||
return context.CompileSharedObject(text, suffix) | |||||
else: | |||||
return context.CompileProg(text, suffix) | |||||
def CheckFunc(context, function_name, header = None, language = None): | |||||
""" | |||||
Configure check for a function "function_name". | |||||
"language" should be "C" or "C++" and is used to select the compiler. | |||||
Default is "C". | |||||
Optional "header" can be defined to define a function prototype, include a | |||||
header file or anything else that comes before main(). | |||||
Sets HAVE_function_name in context.havedict according to the result. | |||||
Note that this uses the current value of compiler and linker flags, make | |||||
sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. | |||||
Returns an empty string for success, an error message for failure. | |||||
""" | |||||
# Remarks from autoconf: | |||||
# - Don't include <ctype.h> because on OSF/1 3.0 it includes <sys/types.h> | |||||
# which includes <sys/select.h> which contains a prototype for select. | |||||
# Similarly for bzero. | |||||
# - assert.h is included to define __stub macros and hopefully few | |||||
# prototypes, which can conflict with char $1(); below. | |||||
# - Override any gcc2 internal prototype to avoid an error. | |||||
# - We use char for the function declaration because int might match the | |||||
# return type of a gcc2 builtin and then its argument prototype would | |||||
# still apply. | |||||
# - The GNU C library defines this for functions which it implements to | |||||
# always fail with ENOSYS. Some functions are actually named something | |||||
# starting with __ and the normal name is an alias. | |||||
if context.headerfilename: | |||||
includetext = '#include "%s"' % context.headerfilename | |||||
else: | |||||
includetext = '' | |||||
if not header: | |||||
header = """ | |||||
#ifdef __cplusplus | |||||
extern "C" | |||||
#endif | |||||
char %s();""" % function_name | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
context.Display("Cannot check for %s(): %s\n" % (function_name, msg)) | |||||
return msg | |||||
text = """ | |||||
%(include)s | |||||
#include <assert.h> | |||||
%(hdr)s | |||||
int main() { | |||||
#if defined (__stub_%(name)s) || defined (__stub___%(name)s) | |||||
fail fail fail | |||||
#else | |||||
%(name)s(); | |||||
#endif | |||||
return 0; | |||||
} | |||||
""" % { 'name': function_name, | |||||
'include': includetext, | |||||
'hdr': header } | |||||
context.Display("Checking for %s function %s()... " % (lang, function_name)) | |||||
ret = context.BuildProg(text, suffix) | |||||
_YesNoResult(context, ret, "HAVE_" + function_name, text, | |||||
"Define to 1 if the system has the function `%s'." %\ | |||||
function_name) | |||||
return ret | |||||
def CheckHeader(context, header_name, header = None, language = None, | |||||
include_quotes = None): | |||||
""" | |||||
Configure check for a C or C++ header file "header_name". | |||||
Optional "header" can be defined to do something before including the | |||||
header file (unusual, supported for consistency). | |||||
"language" should be "C" or "C++" and is used to select the compiler. | |||||
Default is "C". | |||||
Sets HAVE_header_name in context.havedict according to the result. | |||||
Note that this uses the current value of compiler and linker flags, make | |||||
sure $CFLAGS and $CPPFLAGS are set correctly. | |||||
Returns an empty string for success, an error message for failure. | |||||
""" | |||||
# Why compile the program instead of just running the preprocessor? | |||||
# It is possible that the header file exists, but actually using it may | |||||
# fail (e.g., because it depends on other header files). Thus this test is | |||||
# more strict. It may require using the "header" argument. | |||||
# | |||||
# Use <> by default, because the check is normally used for system header | |||||
# files. SCons passes '""' to overrule this. | |||||
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. | |||||
if context.headerfilename: | |||||
includetext = '#include "%s"\n' % context.headerfilename | |||||
else: | |||||
includetext = '' | |||||
if not header: | |||||
header = "" | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
context.Display("Cannot check for header file %s: %s\n" | |||||
% (header_name, msg)) | |||||
return msg | |||||
if not include_quotes: | |||||
include_quotes = "<>" | |||||
text = "%s%s\n#include %s%s%s\n\n" % (includetext, header, | |||||
include_quotes[0], header_name, include_quotes[1]) | |||||
context.Display("Checking for %s header file %s... " % (lang, header_name)) | |||||
ret = context.CompileProg(text, suffix) | |||||
_YesNoResult(context, ret, "HAVE_" + header_name, text, | |||||
"Define to 1 if you have the <%s> header file." % header_name) | |||||
return ret | |||||
def CheckType(context, type_name, fallback = None, | |||||
header = None, language = None): | |||||
""" | |||||
Configure check for a C or C++ type "type_name". | |||||
Optional "header" can be defined to include a header file. | |||||
"language" should be "C" or "C++" and is used to select the compiler. | |||||
Default is "C". | |||||
Sets HAVE_type_name in context.havedict according to the result. | |||||
Note that this uses the current value of compiler and linker flags, make | |||||
sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. | |||||
Returns an empty string for success, an error message for failure. | |||||
""" | |||||
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. | |||||
if context.headerfilename: | |||||
includetext = '#include "%s"' % context.headerfilename | |||||
else: | |||||
includetext = '' | |||||
if not header: | |||||
header = "" | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) | |||||
return msg | |||||
# Remarks from autoconf about this test: | |||||
# - Grepping for the type in include files is not reliable (grep isn't | |||||
# portable anyway). | |||||
# - Using "TYPE my_var;" doesn't work for const qualified types in C++. | |||||
# Adding an initializer is not valid for some C++ classes. | |||||
# - Using the type as parameter to a function either fails for K&$ C or for | |||||
# C++. | |||||
# - Using "TYPE *my_var;" is valid in C for some types that are not | |||||
# declared (struct something). | |||||
# - Using "sizeof(TYPE)" is valid when TYPE is actually a variable. | |||||
# - Using the previous two together works reliably. | |||||
text = """ | |||||
%(include)s | |||||
%(header)s | |||||
int main() { | |||||
if ((%(name)s *) 0) | |||||
return 0; | |||||
if (sizeof (%(name)s)) | |||||
return 0; | |||||
} | |||||
""" % { 'include': includetext, | |||||
'header': header, | |||||
'name': type_name } | |||||
context.Display("Checking for %s type %s... " % (lang, type_name)) | |||||
ret = context.BuildProg(text, suffix) | |||||
_YesNoResult(context, ret, "HAVE_" + type_name, text, | |||||
"Define to 1 if the system has the type `%s'." % type_name) | |||||
if ret and fallback and context.headerfilename: | |||||
f = open(context.headerfilename, "a") | |||||
f.write("typedef %s %s;\n" % (fallback, type_name)) | |||||
f.close() | |||||
return ret | |||||
def CheckTypeSize(context, type_name, header = None, language = None, expect = None): | |||||
"""This check can be used to get the size of a given type, or to check whether | |||||
the type is of expected size. | |||||
Arguments: | |||||
- type : str | |||||
the type to check | |||||
- includes : sequence | |||||
list of headers to include in the test code before testing the type | |||||
- language : str | |||||
'C' or 'C++' | |||||
- expect : int | |||||
if given, will test wether the type has the given number of bytes. | |||||
If not given, will automatically find the size. | |||||
Returns: | |||||
status : int | |||||
0 if the check failed, or the found size of the type if the check succeeded.""" | |||||
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. | |||||
if context.headerfilename: | |||||
includetext = '#include "%s"' % context.headerfilename | |||||
else: | |||||
includetext = '' | |||||
if not header: | |||||
header = "" | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) | |||||
return msg | |||||
src = includetext + header | |||||
if not expect is None: | |||||
# Only check if the given size is the right one | |||||
context.Display('Checking %s is %d bytes... ' % (type_name, expect)) | |||||
# test code taken from autoconf: this is a pretty clever hack to find that | |||||
# a type is of a given size using only compilation. This speeds things up | |||||
# quite a bit compared to straightforward code using TryRun | |||||
src = src + r""" | |||||
typedef %s scons_check_type; | |||||
int main() | |||||
{ | |||||
static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)]; | |||||
test_array[0] = 0; | |||||
return 0; | |||||
} | |||||
""" | |||||
st = context.CompileProg(src % (type_name, expect), suffix) | |||||
if not st: | |||||
context.Display("yes\n") | |||||
_Have(context, "SIZEOF_%s" % type_name, expect, | |||||
"The size of `%s', as computed by sizeof." % type_name) | |||||
return expect | |||||
else: | |||||
context.Display("no\n") | |||||
_LogFailed(context, src, st) | |||||
return 0 | |||||
else: | |||||
# Only check if the given size is the right one | |||||
context.Message('Checking size of %s ... ' % type_name) | |||||
# We have to be careful with the program we wish to test here since | |||||
# compilation will be attempted using the current environment's flags. | |||||
# So make sure that the program will compile without any warning. For | |||||
# example using: 'int main(int argc, char** argv)' will fail with the | |||||
# '-Wall -Werror' flags since the variables argc and argv would not be | |||||
# used in the program... | |||||
# | |||||
src = src + """ | |||||
#include <stdlib.h> | |||||
#include <stdio.h> | |||||
int main() { | |||||
printf("%d", (int)sizeof(""" + type_name + """)); | |||||
return 0; | |||||
} | |||||
""" | |||||
st, out = context.RunProg(src, suffix) | |||||
try: | |||||
size = int(out) | |||||
except ValueError: | |||||
# If cannot convert output of test prog to an integer (the size), | |||||
# something went wront, so just fail | |||||
st = 1 | |||||
size = 0 | |||||
if not st: | |||||
context.Display("yes\n") | |||||
_Have(context, "SIZEOF_%s" % type_name, size, | |||||
"The size of `%s', as computed by sizeof." % type_name) | |||||
return size | |||||
else: | |||||
context.Display("no\n") | |||||
_LogFailed(context, src, st) | |||||
return 0 | |||||
return 0 | |||||
def CheckDeclaration(context, symbol, includes = None, language = None): | |||||
"""Checks whether symbol is declared. | |||||
Use the same test as autoconf, that is test whether the symbol is defined | |||||
as a macro or can be used as an r-value. | |||||
Arguments: | |||||
symbol : str | |||||
the symbol to check | |||||
includes : str | |||||
Optional "header" can be defined to include a header file. | |||||
language : str | |||||
only C and C++ supported. | |||||
Returns: | |||||
status : bool | |||||
True if the check failed, False if succeeded.""" | |||||
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. | |||||
if context.headerfilename: | |||||
includetext = '#include "%s"' % context.headerfilename | |||||
else: | |||||
includetext = '' | |||||
if not includes: | |||||
includes = "" | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
context.Display("Cannot check for declaration %s: %s\n" % (symbol, msg)) | |||||
return msg | |||||
src = includetext + includes | |||||
context.Display('Checking whether %s is declared... ' % symbol) | |||||
src = src + r""" | |||||
int main() | |||||
{ | |||||
#ifndef %s | |||||
(void) %s; | |||||
#endif | |||||
; | |||||
return 0; | |||||
} | |||||
""" % (symbol, symbol) | |||||
st = context.CompileProg(src, suffix) | |||||
_YesNoResult(context, st, "HAVE_DECL_" + symbol, src, | |||||
"Set to 1 if %s is defined." % symbol) | |||||
return st | |||||
def CheckLib(context, libs, func_name = None, header = None, | |||||
extra_libs = None, call = None, language = None, autoadd = 1, | |||||
append = True): | |||||
""" | |||||
Configure check for a C or C++ libraries "libs". Searches through | |||||
the list of libraries, until one is found where the test succeeds. | |||||
Tests if "func_name" or "call" exists in the library. Note: if it exists | |||||
in another library the test succeeds anyway! | |||||
Optional "header" can be defined to include a header file. If not given a | |||||
default prototype for "func_name" is added. | |||||
Optional "extra_libs" is a list of library names to be added after | |||||
"lib_name" in the build command. To be used for libraries that "lib_name" | |||||
depends on. | |||||
Optional "call" replaces the call to "func_name" in the test code. It must | |||||
consist of complete C statements, including a trailing ";". | |||||
Both "func_name" and "call" arguments are optional, and in that case, just | |||||
linking against the libs is tested. | |||||
"language" should be "C" or "C++" and is used to select the compiler. | |||||
Default is "C". | |||||
Note that this uses the current value of compiler and linker flags, make | |||||
sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. | |||||
Returns an empty string for success, an error message for failure. | |||||
""" | |||||
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. | |||||
if context.headerfilename: | |||||
includetext = '#include "%s"' % context.headerfilename | |||||
else: | |||||
includetext = '' | |||||
if not header: | |||||
header = "" | |||||
text = """ | |||||
%s | |||||
%s""" % (includetext, header) | |||||
# Add a function declaration if needed. | |||||
if func_name and func_name != "main": | |||||
if not header: | |||||
text = text + """ | |||||
#ifdef __cplusplus | |||||
extern "C" | |||||
#endif | |||||
char %s(); | |||||
""" % func_name | |||||
# The actual test code. | |||||
if not call: | |||||
call = "%s();" % func_name | |||||
# if no function to test, leave main() blank | |||||
text = text + """ | |||||
int | |||||
main() { | |||||
%s | |||||
return 0; | |||||
} | |||||
""" % (call or "") | |||||
if call: | |||||
i = call.find("\n") | |||||
if i > 0: | |||||
calltext = call[:i] + ".." | |||||
elif call[-1] == ';': | |||||
calltext = call[:-1] | |||||
else: | |||||
calltext = call | |||||
for lib_name in libs: | |||||
lang, suffix, msg = _lang2suffix(language) | |||||
if msg: | |||||
context.Display("Cannot check for library %s: %s\n" % (lib_name, msg)) | |||||
return msg | |||||
# if a function was specified to run in main(), say it | |||||
if call: | |||||
context.Display("Checking for %s in %s library %s... " | |||||
% (calltext, lang, lib_name)) | |||||
# otherwise, just say the name of library and language | |||||
else: | |||||
context.Display("Checking for %s library %s... " | |||||
% (lang, lib_name)) | |||||
if lib_name: | |||||
l = [ lib_name ] | |||||
if extra_libs: | |||||
l.extend(extra_libs) | |||||
if append: | |||||
oldLIBS = context.AppendLIBS(l) | |||||
else: | |||||
oldLIBS = context.PrependLIBS(l) | |||||
sym = "HAVE_LIB" + lib_name | |||||
else: | |||||
oldLIBS = -1 | |||||
sym = None | |||||
ret = context.BuildProg(text, suffix) | |||||
_YesNoResult(context, ret, sym, text, | |||||
"Define to 1 if you have the `%s' library." % lib_name) | |||||
if oldLIBS != -1 and (ret or not autoadd): | |||||
context.SetLIBS(oldLIBS) | |||||
if not ret: | |||||
return ret | |||||
return ret | |||||
def CheckProg(context, prog_name): | |||||
""" | |||||
Configure check for a specific program. | |||||
Check whether program prog_name exists in path. If it is found, | |||||
returns the path for it, otherwise returns None. | |||||
""" | |||||
context.Display("Checking whether %s program exists..." % prog_name) | |||||
path = context.env.WhereIs(prog_name) | |||||
if path: | |||||
context.Display(path + "\n") | |||||
else: | |||||
context.Display("no\n") | |||||
return path | |||||
# | |||||
# END OF PUBLIC FUNCTIONS | |||||
# | |||||
def _YesNoResult(context, ret, key, text, comment = None): | |||||
""" | |||||
Handle the result of a test with a "yes" or "no" result. | |||||
:Parameters: | |||||
- `ret` is the return value: empty if OK, error message when not. | |||||
- `key` is the name of the symbol to be defined (HAVE_foo). | |||||
- `text` is the source code of the program used for testing. | |||||
- `comment` is the C comment to add above the line defining the symbol (the comment is automatically put inside a /\* \*/). If None, no comment is added. | |||||
""" | |||||
if key: | |||||
_Have(context, key, not ret, comment) | |||||
if ret: | |||||
context.Display("no\n") | |||||
_LogFailed(context, text, ret) | |||||
else: | |||||
context.Display("yes\n") | |||||
def _Have(context, key, have, comment = None): | |||||
""" | |||||
Store result of a test in context.havedict and context.headerfilename. | |||||
:Parameters: | |||||
- `key` - is a "HAVE_abc" name. It is turned into all CAPITALS and non-alphanumerics are replaced by an underscore. | |||||
- `have` - value as it should appear in the header file, include quotes when desired and escape special characters! | |||||
- `comment` is the C comment to add above the line defining the symbol (the comment is automatically put inside a /\* \*/). If None, no comment is added. | |||||
The value of "have" can be: | |||||
- 1 - Feature is defined, add "#define key". | |||||
- 0 - Feature is not defined, add "/\* #undef key \*/". Adding "undef" is what autoconf does. Not useful for the compiler, but it shows that the test was done. | |||||
- number - Feature is defined to this number "#define key have". Doesn't work for 0 or 1, use a string then. | |||||
- string - Feature is defined to this string "#define key have". | |||||
""" | |||||
key_up = key.upper() | |||||
key_up = re.sub('[^A-Z0-9_]', '_', key_up) | |||||
context.havedict[key_up] = have | |||||
if have == 1: | |||||
line = "#define %s 1\n" % key_up | |||||
elif have == 0: | |||||
line = "/* #undef %s */\n" % key_up | |||||
elif isinstance(have, int): | |||||
line = "#define %s %d\n" % (key_up, have) | |||||
else: | |||||
line = "#define %s %s\n" % (key_up, str(have)) | |||||
if comment is not None: | |||||
lines = "\n/* %s */\n" % comment + line | |||||
else: | |||||
lines = "\n" + line | |||||
if context.headerfilename: | |||||
f = open(context.headerfilename, "a") | |||||
f.write(lines) | |||||
f.close() | |||||
elif hasattr(context,'config_h'): | |||||
context.config_h = context.config_h + lines | |||||
def _LogFailed(context, text, msg): | |||||
""" | |||||
Write to the log about a failed program. | |||||
Add line numbers, so that error messages can be understood. | |||||
""" | |||||
if LogInputFiles: | |||||
context.Log("Failed program was:\n") | |||||
lines = text.split('\n') | |||||
if len(lines) and lines[-1] == '': | |||||
lines = lines[:-1] # remove trailing empty line | |||||
n = 1 | |||||
for line in lines: | |||||
context.Log("%d: %s\n" % (n, line)) | |||||
n = n + 1 | |||||
if LogErrorMessages: | |||||
context.Log("Error message: %s\n" % msg) | |||||
def _lang2suffix(lang): | |||||
""" | |||||
Convert a language name to a suffix. | |||||
When "lang" is empty or None C is assumed. | |||||
Returns a tuple (lang, suffix, None) when it works. | |||||
For an unrecognized language returns (None, None, msg). | |||||
Where: | |||||
- lang = the unified language name | |||||
- suffix = the suffix, including the leading dot | |||||
- msg = an error message | |||||
""" | |||||
if not lang or lang in ["C", "c"]: | |||||
return ("C", ".c", None) | |||||
if lang in ["c++", "C++", "cpp", "CXX", "cxx"]: | |||||
return ("C++", ".cpp", None) | |||||
return None, None, "Unsupported language: %s" % lang | |||||
# vim: set sw=4 et sts=4 tw=79 fo+=l: | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,243 @@ | |||||
"""SCons.Debug | |||||
Code for debugging SCons internal things. Shouldn't be | |||||
needed by most users. Quick shortcuts: | |||||
from SCons.Debug import caller_trace | |||||
caller_trace() | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Debug.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import sys | |||||
import time | |||||
import weakref | |||||
import inspect | |||||
# Global variable that gets set to 'True' by the Main script, | |||||
# when the creation of class instances should get tracked. | |||||
track_instances = False | |||||
# List of currently tracked classes | |||||
tracked_classes = {} | |||||
def logInstanceCreation(instance, name=None): | |||||
if name is None: | |||||
name = instance.__class__.__name__ | |||||
if name not in tracked_classes: | |||||
tracked_classes[name] = [] | |||||
if hasattr(instance, '__dict__'): | |||||
tracked_classes[name].append(weakref.ref(instance)) | |||||
else: | |||||
# weakref doesn't seem to work when the instance | |||||
# contains only slots... | |||||
tracked_classes[name].append(instance) | |||||
def string_to_classes(s): | |||||
if s == '*': | |||||
return sorted(tracked_classes.keys()) | |||||
else: | |||||
return s.split() | |||||
def fetchLoggedInstances(classes="*"): | |||||
classnames = string_to_classes(classes) | |||||
return [(cn, len(tracked_classes[cn])) for cn in classnames] | |||||
def countLoggedInstances(classes, file=sys.stdout): | |||||
for classname in string_to_classes(classes): | |||||
file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) | |||||
def listLoggedInstances(classes, file=sys.stdout): | |||||
for classname in string_to_classes(classes): | |||||
file.write('\n%s:\n' % classname) | |||||
for ref in tracked_classes[classname]: | |||||
if inspect.isclass(ref): | |||||
obj = ref() | |||||
else: | |||||
obj = ref | |||||
if obj is not None: | |||||
file.write(' %s\n' % repr(obj)) | |||||
def dumpLoggedInstances(classes, file=sys.stdout): | |||||
for classname in string_to_classes(classes): | |||||
file.write('\n%s:\n' % classname) | |||||
for ref in tracked_classes[classname]: | |||||
obj = ref() | |||||
if obj is not None: | |||||
file.write(' %s:\n' % obj) | |||||
for key, value in obj.__dict__.items(): | |||||
file.write(' %20s : %s\n' % (key, value)) | |||||
if sys.platform[:5] == "linux": | |||||
# Linux doesn't actually support memory usage stats from getrusage(). | |||||
def memory(): | |||||
with open('/proc/self/stat') as f: | |||||
mstr = f.read() | |||||
mstr = mstr.split()[22] | |||||
return int(mstr) | |||||
elif sys.platform[:6] == 'darwin': | |||||
#TODO really get memory stats for OS X | |||||
def memory(): | |||||
return 0 | |||||
else: | |||||
try: | |||||
import resource | |||||
except ImportError: | |||||
try: | |||||
import win32process | |||||
import win32api | |||||
except ImportError: | |||||
def memory(): | |||||
return 0 | |||||
else: | |||||
def memory(): | |||||
process_handle = win32api.GetCurrentProcess() | |||||
memory_info = win32process.GetProcessMemoryInfo( process_handle ) | |||||
return memory_info['PeakWorkingSetSize'] | |||||
else: | |||||
def memory(): | |||||
res = resource.getrusage(resource.RUSAGE_SELF) | |||||
return res[4] | |||||
# returns caller's stack | |||||
def caller_stack(): | |||||
import traceback | |||||
tb = traceback.extract_stack() | |||||
# strip itself and the caller from the output | |||||
tb = tb[:-2] | |||||
result = [] | |||||
for back in tb: | |||||
# (filename, line number, function name, text) | |||||
key = back[:3] | |||||
result.append('%s:%d(%s)' % func_shorten(key)) | |||||
return result | |||||
caller_bases = {} | |||||
caller_dicts = {} | |||||
def caller_trace(back=0): | |||||
""" | |||||
Trace caller stack and save info into global dicts, which | |||||
are printed automatically at the end of SCons execution. | |||||
""" | |||||
global caller_bases, caller_dicts | |||||
import traceback | |||||
tb = traceback.extract_stack(limit=3+back) | |||||
tb.reverse() | |||||
callee = tb[1][:3] | |||||
caller_bases[callee] = caller_bases.get(callee, 0) + 1 | |||||
for caller in tb[2:]: | |||||
caller = callee + caller[:3] | |||||
try: | |||||
entry = caller_dicts[callee] | |||||
except KeyError: | |||||
caller_dicts[callee] = entry = {} | |||||
entry[caller] = entry.get(caller, 0) + 1 | |||||
callee = caller | |||||
# print a single caller and its callers, if any | |||||
def _dump_one_caller(key, file, level=0): | |||||
leader = ' '*level | |||||
for v,c in sorted([(-v,c) for c,v in caller_dicts[key].items()]): | |||||
file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:]))) | |||||
if c in caller_dicts: | |||||
_dump_one_caller(c, file, level+1) | |||||
# print each call tree | |||||
def dump_caller_counts(file=sys.stdout): | |||||
for k in sorted(caller_bases.keys()): | |||||
file.write("Callers of %s:%d(%s), %d calls:\n" | |||||
% (func_shorten(k) + (caller_bases[k],))) | |||||
_dump_one_caller(k, file) | |||||
shorten_list = [ | |||||
( '/scons/SCons/', 1), | |||||
( '/src/engine/SCons/', 1), | |||||
( '/usr/lib/python', 0), | |||||
] | |||||
if os.sep != '/': | |||||
shorten_list = [(t[0].replace('/', os.sep), t[1]) for t in shorten_list] | |||||
def func_shorten(func_tuple): | |||||
f = func_tuple[0] | |||||
for t in shorten_list: | |||||
i = f.find(t[0]) | |||||
if i >= 0: | |||||
if t[1]: | |||||
i = i + len(t[0]) | |||||
return (f[i:],)+func_tuple[1:] | |||||
return func_tuple | |||||
TraceFP = {} | |||||
if sys.platform == 'win32': | |||||
TraceDefault = 'con' | |||||
else: | |||||
TraceDefault = '/dev/tty' | |||||
TimeStampDefault = None | |||||
StartTime = time.time() | |||||
PreviousTime = StartTime | |||||
def Trace(msg, file=None, mode='w', tstamp=None): | |||||
"""Write a trace message to a file. Whenever a file is specified, | |||||
it becomes the default for the next call to Trace().""" | |||||
global TraceDefault | |||||
global TimeStampDefault | |||||
global PreviousTime | |||||
if file is None: | |||||
file = TraceDefault | |||||
else: | |||||
TraceDefault = file | |||||
if tstamp is None: | |||||
tstamp = TimeStampDefault | |||||
else: | |||||
TimeStampDefault = tstamp | |||||
try: | |||||
fp = TraceFP[file] | |||||
except KeyError: | |||||
try: | |||||
fp = TraceFP[file] = open(file, mode) | |||||
except TypeError: | |||||
# Assume we were passed an open file pointer. | |||||
fp = file | |||||
if tstamp: | |||||
now = time.time() | |||||
fp.write('%8.4f %8.4f: ' % (now - StartTime, now - PreviousTime)) | |||||
PreviousTime = now | |||||
fp.write(msg) | |||||
fp.flush() | |||||
fp.close() | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,593 @@ | |||||
"""SCons.Defaults | |||||
Builders and other things for the local site. Here's where we'll | |||||
duplicate the functionality of autoconf until we move it into the | |||||
installation procedure or use something like qmconf. | |||||
The code that reads the registry to find MSVC components was borrowed | |||||
from distutils.msvccompiler. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
from __future__ import division | |||||
__revision__ = "src/engine/SCons/Defaults.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import errno | |||||
import shutil | |||||
import stat | |||||
import time | |||||
import sys | |||||
import SCons.Action | |||||
import SCons.Builder | |||||
import SCons.CacheDir | |||||
import SCons.Environment | |||||
import SCons.PathList | |||||
import SCons.Subst | |||||
import SCons.Tool | |||||
# A placeholder for a default Environment (for fetching source files | |||||
# from source code management systems and the like). This must be | |||||
# initialized later, after the top-level directory is set by the calling | |||||
# interface. | |||||
_default_env = None | |||||
# Lazily instantiate the default environment so the overhead of creating | |||||
# it doesn't apply when it's not needed. | |||||
def _fetch_DefaultEnvironment(*args, **kw): | |||||
""" | |||||
Returns the already-created default construction environment. | |||||
""" | |||||
global _default_env | |||||
return _default_env | |||||
def DefaultEnvironment(*args, **kw): | |||||
""" | |||||
Initial public entry point for creating the default construction | |||||
Environment. | |||||
After creating the environment, we overwrite our name | |||||
(DefaultEnvironment) with the _fetch_DefaultEnvironment() function, | |||||
which more efficiently returns the initialized default construction | |||||
environment without checking for its existence. | |||||
(This function still exists with its _default_check because someone | |||||
else (*cough* Script/__init__.py *cough*) may keep a reference | |||||
to this function. So we can't use the fully functional idiom of | |||||
having the name originally be a something that *only* creates the | |||||
construction environment and then overwrites the name.) | |||||
""" | |||||
global _default_env | |||||
if not _default_env: | |||||
import SCons.Util | |||||
_default_env = SCons.Environment.Environment(*args, **kw) | |||||
if SCons.Util.md5: | |||||
_default_env.Decider('MD5') | |||||
else: | |||||
_default_env.Decider('timestamp-match') | |||||
global DefaultEnvironment | |||||
DefaultEnvironment = _fetch_DefaultEnvironment | |||||
_default_env._CacheDir_path = None | |||||
return _default_env | |||||
# Emitters for setting the shared attribute on object files, | |||||
# and an action for checking that all of the source files | |||||
# going into a shared library are, in fact, shared. | |||||
def StaticObjectEmitter(target, source, env): | |||||
for tgt in target: | |||||
tgt.attributes.shared = None | |||||
return (target, source) | |||||
def SharedObjectEmitter(target, source, env): | |||||
for tgt in target: | |||||
tgt.attributes.shared = 1 | |||||
return (target, source) | |||||
def SharedFlagChecker(source, target, env): | |||||
same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') | |||||
if same == '0' or same == '' or same == 'False': | |||||
for src in source: | |||||
try: | |||||
shared = src.attributes.shared | |||||
except AttributeError: | |||||
shared = None | |||||
if not shared: | |||||
raise SCons.Errors.UserError("Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])) | |||||
SharedCheck = SCons.Action.Action(SharedFlagChecker, None) | |||||
# Some people were using these variable name before we made | |||||
# SourceFileScanner part of the public interface. Don't break their | |||||
# SConscript files until we've given them some fair warning and a | |||||
# transition period. | |||||
CScan = SCons.Tool.CScanner | |||||
DScan = SCons.Tool.DScanner | |||||
LaTeXScan = SCons.Tool.LaTeXScanner | |||||
ObjSourceScan = SCons.Tool.SourceFileScanner | |||||
ProgScan = SCons.Tool.ProgramScanner | |||||
# These aren't really tool scanners, so they don't quite belong with | |||||
# the rest of those in Tool/__init__.py, but I'm not sure where else | |||||
# they should go. Leave them here for now. | |||||
import SCons.Scanner.Dir | |||||
DirScanner = SCons.Scanner.Dir.DirScanner() | |||||
DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner() | |||||
# Actions for common languages. | |||||
CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR") | |||||
ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR") | |||||
CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR") | |||||
ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR") | |||||
DAction = SCons.Action.Action("$DCOM", "$DCOMSTR") | |||||
ShDAction = SCons.Action.Action("$SHDCOM", "$SHDCOMSTR") | |||||
ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR") | |||||
ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") | |||||
LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") | |||||
ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") | |||||
LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") | |||||
# Common tasks that we allow users to perform in platform-independent | |||||
# ways by creating ActionFactory instances. | |||||
ActionFactory = SCons.Action.ActionFactory | |||||
def get_paths_str(dest): | |||||
# If dest is a list, we need to manually call str() on each element | |||||
if SCons.Util.is_List(dest): | |||||
elem_strs = [] | |||||
for element in dest: | |||||
elem_strs.append('"' + str(element) + '"') | |||||
return '[' + ', '.join(elem_strs) + ']' | |||||
else: | |||||
return '"' + str(dest) + '"' | |||||
permission_dic = { | |||||
'u':{ | |||||
'r':stat.S_IRUSR, | |||||
'w':stat.S_IWUSR, | |||||
'x':stat.S_IXUSR | |||||
}, | |||||
'g':{ | |||||
'r':stat.S_IRGRP, | |||||
'w':stat.S_IWGRP, | |||||
'x':stat.S_IXGRP | |||||
}, | |||||
'o':{ | |||||
'r':stat.S_IROTH, | |||||
'w':stat.S_IWOTH, | |||||
'x':stat.S_IXOTH | |||||
} | |||||
} | |||||
def chmod_func(dest, mode): | |||||
import SCons.Util | |||||
from string import digits | |||||
SCons.Node.FS.invalidate_node_memos(dest) | |||||
if not SCons.Util.is_List(dest): | |||||
dest = [dest] | |||||
if SCons.Util.is_String(mode) and not 0 in [i in digits for i in mode]: | |||||
mode = int(mode, 8) | |||||
if not SCons.Util.is_String(mode): | |||||
for element in dest: | |||||
os.chmod(str(element), mode) | |||||
else: | |||||
mode = str(mode) | |||||
for operation in mode.split(","): | |||||
if "=" in operation: | |||||
operator = "=" | |||||
elif "+" in operation: | |||||
operator = "+" | |||||
elif "-" in operation: | |||||
operator = "-" | |||||
else: | |||||
raise SyntaxError("Could not find +, - or =") | |||||
operation_list = operation.split(operator) | |||||
if len(operation_list) is not 2: | |||||
raise SyntaxError("More than one operator found") | |||||
user = operation_list[0].strip().replace("a", "ugo") | |||||
permission = operation_list[1].strip() | |||||
new_perm = 0 | |||||
for u in user: | |||||
for p in permission: | |||||
try: | |||||
new_perm = new_perm | permission_dic[u][p] | |||||
except KeyError: | |||||
raise SyntaxError("Unrecognized user or permission format") | |||||
for element in dest: | |||||
curr_perm = os.stat(str(element)).st_mode | |||||
if operator == "=": | |||||
os.chmod(str(element), new_perm) | |||||
elif operator == "+": | |||||
os.chmod(str(element), curr_perm | new_perm) | |||||
elif operator == "-": | |||||
os.chmod(str(element), curr_perm & ~new_perm) | |||||
def chmod_strfunc(dest, mode): | |||||
import SCons.Util | |||||
if not SCons.Util.is_String(mode): | |||||
return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode) | |||||
else: | |||||
return 'Chmod(%s, "%s")' % (get_paths_str(dest), str(mode)) | |||||
Chmod = ActionFactory(chmod_func, chmod_strfunc) | |||||
def copy_func(dest, src, symlinks=True): | |||||
""" | |||||
If symlinks (is true), then a symbolic link will be | |||||
shallow copied and recreated as a symbolic link; otherwise, copying | |||||
a symbolic link will be equivalent to copying the symbolic link's | |||||
final target regardless of symbolic link depth. | |||||
""" | |||||
dest = str(dest) | |||||
src = str(src) | |||||
SCons.Node.FS.invalidate_node_memos(dest) | |||||
if SCons.Util.is_List(src) and os.path.isdir(dest): | |||||
for file in src: | |||||
shutil.copy2(file, dest) | |||||
return 0 | |||||
elif os.path.islink(src): | |||||
if symlinks: | |||||
return os.symlink(os.readlink(src), dest) | |||||
else: | |||||
return copy_func(dest, os.path.realpath(src)) | |||||
elif os.path.isfile(src): | |||||
shutil.copy2(src, dest) | |||||
return 0 | |||||
else: | |||||
shutil.copytree(src, dest, symlinks) | |||||
# copytree returns None in python2 and destination string in python3 | |||||
# A error is raised in both cases, so we can just return 0 for success | |||||
return 0 | |||||
Copy = ActionFactory( | |||||
copy_func, | |||||
lambda dest, src, symlinks=True: 'Copy("%s", "%s")' % (dest, src) | |||||
) | |||||
def delete_func(dest, must_exist=0): | |||||
SCons.Node.FS.invalidate_node_memos(dest) | |||||
if not SCons.Util.is_List(dest): | |||||
dest = [dest] | |||||
for entry in dest: | |||||
entry = str(entry) | |||||
# os.path.exists returns False with broken links that exist | |||||
entry_exists = os.path.exists(entry) or os.path.islink(entry) | |||||
if not entry_exists and not must_exist: | |||||
continue | |||||
# os.path.isdir returns True when entry is a link to a dir | |||||
if os.path.isdir(entry) and not os.path.islink(entry): | |||||
shutil.rmtree(entry, 1) | |||||
continue | |||||
os.unlink(entry) | |||||
def delete_strfunc(dest, must_exist=0): | |||||
return 'Delete(%s)' % get_paths_str(dest) | |||||
Delete = ActionFactory(delete_func, delete_strfunc) | |||||
def mkdir_func(dest): | |||||
SCons.Node.FS.invalidate_node_memos(dest) | |||||
if not SCons.Util.is_List(dest): | |||||
dest = [dest] | |||||
for entry in dest: | |||||
try: | |||||
os.makedirs(str(entry)) | |||||
except os.error as e: | |||||
p = str(entry) | |||||
if (e.args[0] == errno.EEXIST or | |||||
(sys.platform=='win32' and e.args[0]==183)) \ | |||||
and os.path.isdir(str(entry)): | |||||
pass # not an error if already exists | |||||
else: | |||||
raise | |||||
Mkdir = ActionFactory(mkdir_func, | |||||
lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) | |||||
def move_func(dest, src): | |||||
SCons.Node.FS.invalidate_node_memos(dest) | |||||
SCons.Node.FS.invalidate_node_memos(src) | |||||
shutil.move(src, dest) | |||||
Move = ActionFactory(move_func, | |||||
lambda dest, src: 'Move("%s", "%s")' % (dest, src), | |||||
convert=str) | |||||
def touch_func(dest): | |||||
SCons.Node.FS.invalidate_node_memos(dest) | |||||
if not SCons.Util.is_List(dest): | |||||
dest = [dest] | |||||
for file in dest: | |||||
file = str(file) | |||||
mtime = int(time.time()) | |||||
if os.path.exists(file): | |||||
atime = os.path.getatime(file) | |||||
else: | |||||
open(file, 'w') | |||||
atime = mtime | |||||
os.utime(file, (atime, mtime)) | |||||
Touch = ActionFactory(touch_func, | |||||
lambda file: 'Touch(%s)' % get_paths_str(file)) | |||||
# Internal utility functions | |||||
def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): | |||||
""" | |||||
Creates a new list from 'list' by first interpolating each element | |||||
in the list using the 'env' dictionary and then calling f on the | |||||
list, and finally calling _concat_ixes to concatenate 'prefix' and | |||||
'suffix' onto each element of the list. | |||||
""" | |||||
if not list: | |||||
return list | |||||
l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) | |||||
if l is not None: | |||||
list = l | |||||
return _concat_ixes(prefix, list, suffix, env) | |||||
def _concat_ixes(prefix, list, suffix, env): | |||||
""" | |||||
Creates a new list from 'list' by concatenating the 'prefix' and | |||||
'suffix' arguments onto each element of the list. A trailing space | |||||
on 'prefix' or leading space on 'suffix' will cause them to be put | |||||
into separate list elements rather than being concatenated. | |||||
""" | |||||
result = [] | |||||
# ensure that prefix and suffix are strings | |||||
prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) | |||||
suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) | |||||
for x in list: | |||||
if isinstance(x, SCons.Node.FS.File): | |||||
result.append(x) | |||||
continue | |||||
x = str(x) | |||||
if x: | |||||
if prefix: | |||||
if prefix[-1] == ' ': | |||||
result.append(prefix[:-1]) | |||||
elif x[:len(prefix)] != prefix: | |||||
x = prefix + x | |||||
result.append(x) | |||||
if suffix: | |||||
if suffix[0] == ' ': | |||||
result.append(suffix[1:]) | |||||
elif x[-len(suffix):] != suffix: | |||||
result[-1] = result[-1]+suffix | |||||
return result | |||||
def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): | |||||
""" | |||||
This is a wrapper around _concat()/_concat_ixes() that checks for | |||||
the existence of prefixes or suffixes on list items and strips them | |||||
where it finds them. This is used by tools (like the GNU linker) | |||||
that need to turn something like 'libfoo.a' into '-lfoo'. | |||||
""" | |||||
if not itms: | |||||
return itms | |||||
if not callable(c): | |||||
env_c = env['_concat'] | |||||
if env_c != _concat and callable(env_c): | |||||
# There's a custom _concat() method in the construction | |||||
# environment, and we've allowed people to set that in | |||||
# the past (see test/custom-concat.py), so preserve the | |||||
# backwards compatibility. | |||||
c = env_c | |||||
else: | |||||
c = _concat_ixes | |||||
stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) | |||||
stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) | |||||
stripped = [] | |||||
for l in SCons.PathList.PathList(itms).subst_path(env, None, None): | |||||
if isinstance(l, SCons.Node.FS.File): | |||||
stripped.append(l) | |||||
continue | |||||
if not SCons.Util.is_String(l): | |||||
l = str(l) | |||||
for stripprefix in stripprefixes: | |||||
lsp = len(stripprefix) | |||||
if l[:lsp] == stripprefix: | |||||
l = l[lsp:] | |||||
# Do not strip more than one prefix | |||||
break | |||||
for stripsuffix in stripsuffixes: | |||||
lss = len(stripsuffix) | |||||
if l[-lss:] == stripsuffix: | |||||
l = l[:-lss] | |||||
# Do not strip more than one suffix | |||||
break | |||||
stripped.append(l) | |||||
return c(prefix, stripped, suffix, env) | |||||
def processDefines(defs): | |||||
"""process defines, resolving strings, lists, dictionaries, into a list of | |||||
strings | |||||
""" | |||||
if SCons.Util.is_List(defs): | |||||
l = [] | |||||
for d in defs: | |||||
if d is None: | |||||
continue | |||||
elif SCons.Util.is_List(d) or isinstance(d, tuple): | |||||
if len(d) >= 2: | |||||
l.append(str(d[0]) + '=' + str(d[1])) | |||||
else: | |||||
l.append(str(d[0])) | |||||
elif SCons.Util.is_Dict(d): | |||||
for macro,value in d.items(): | |||||
if value is not None: | |||||
l.append(str(macro) + '=' + str(value)) | |||||
else: | |||||
l.append(str(macro)) | |||||
elif SCons.Util.is_String(d): | |||||
l.append(str(d)) | |||||
else: | |||||
raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) | |||||
elif SCons.Util.is_Dict(defs): | |||||
# The items in a dictionary are stored in random order, but | |||||
# if the order of the command-line options changes from | |||||
# invocation to invocation, then the signature of the command | |||||
# line will change and we'll get random unnecessary rebuilds. | |||||
# Consequently, we have to sort the keys to ensure a | |||||
# consistent order... | |||||
l = [] | |||||
for k,v in sorted(defs.items()): | |||||
if v is None: | |||||
l.append(str(k)) | |||||
else: | |||||
l.append(str(k) + '=' + str(v)) | |||||
else: | |||||
l = [str(defs)] | |||||
return l | |||||
def _defines(prefix, defs, suffix, env, c=_concat_ixes): | |||||
"""A wrapper around _concat_ixes that turns a list or string | |||||
into a list of C preprocessor command-line definitions. | |||||
""" | |||||
return c(prefix, env.subst_path(processDefines(defs)), suffix, env) | |||||
class NullCmdGenerator(object): | |||||
"""This is a callable class that can be used in place of other | |||||
command generators if you don't want them to do anything. | |||||
The __call__ method for this class simply returns the thing | |||||
you instantiated it with. | |||||
Example usage: | |||||
env["DO_NOTHING"] = NullCmdGenerator | |||||
env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" | |||||
""" | |||||
def __init__(self, cmd): | |||||
self.cmd = cmd | |||||
def __call__(self, target, source, env, for_signature=None): | |||||
return self.cmd | |||||
class Variable_Method_Caller(object): | |||||
"""A class for finding a construction variable on the stack and | |||||
calling one of its methods. | |||||
We use this to support "construction variables" in our string | |||||
eval()s that actually stand in for methods--specifically, use | |||||
of "RDirs" in call to _concat that should actually execute the | |||||
"TARGET.RDirs" method. (We used to support this by creating a little | |||||
"build dictionary" that mapped RDirs to the method, but this got in | |||||
the way of Memoizing construction environments, because we had to | |||||
create new environment objects to hold the variables.) | |||||
""" | |||||
def __init__(self, variable, method): | |||||
self.variable = variable | |||||
self.method = method | |||||
def __call__(self, *args, **kw): | |||||
try: 1//0 | |||||
except ZeroDivisionError: | |||||
# Don't start iterating with the current stack-frame to | |||||
# prevent creating reference cycles (f_back is safe). | |||||
frame = sys.exc_info()[2].tb_frame.f_back | |||||
variable = self.variable | |||||
while frame: | |||||
if variable in frame.f_locals: | |||||
v = frame.f_locals[variable] | |||||
if v: | |||||
method = getattr(v, self.method) | |||||
return method(*args, **kw) | |||||
frame = frame.f_back | |||||
return None | |||||
# if $version_var is not empty, returns env[flags_var], otherwise returns None | |||||
def __libversionflags(env, version_var, flags_var): | |||||
try: | |||||
if env.subst('$'+version_var): | |||||
return env[flags_var] | |||||
except KeyError: | |||||
pass | |||||
return None | |||||
ConstructionEnvironment = { | |||||
'BUILDERS' : {}, | |||||
'SCANNERS' : [ SCons.Tool.SourceFileScanner ], | |||||
'CONFIGUREDIR' : '#/.sconf_temp', | |||||
'CONFIGURELOG' : '#/config.log', | |||||
'CPPSUFFIXES' : SCons.Tool.CSuffixes, | |||||
'DSUFFIXES' : SCons.Tool.DSuffixes, | |||||
'ENV' : {}, | |||||
'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, | |||||
# 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions | |||||
'_concat' : _concat, | |||||
'_defines' : _defines, | |||||
'_stripixes' : _stripixes, | |||||
'_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', | |||||
'_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', | |||||
'_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', | |||||
'_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', | |||||
'__libversionflags' : __libversionflags, | |||||
'__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', | |||||
'__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', | |||||
'__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', | |||||
'TEMPFILE' : NullCmdGenerator, | |||||
'Dir' : Variable_Method_Caller('TARGET', 'Dir'), | |||||
'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), | |||||
'File' : Variable_Method_Caller('TARGET', 'File'), | |||||
'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), | |||||
} | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,229 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
"""SCons.Errors | |||||
This file contains the exception classes used to handle internal | |||||
and user errors in SCons. | |||||
""" | |||||
__revision__ = "src/engine/SCons/Errors.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import shutil | |||||
import SCons.Util | |||||
class BuildError(Exception): | |||||
""" Errors occurring while building. | |||||
BuildError have the following attributes: | |||||
========================================= | |||||
Information about the cause of the build error: | |||||
----------------------------------------------- | |||||
errstr : a description of the error message | |||||
status : the return code of the action that caused the build error. | |||||
Must be set to a non-zero value even if the build error is not due | |||||
to an action returning a non-zero returned code. | |||||
exitstatus : SCons exit status due to this build error. | |||||
Must be nonzero unless due to an explicit Exit() | |||||
call. Not always the same as status, since | |||||
actions return a status code that should be | |||||
respected, but SCons typically exits with 2 | |||||
irrespective of the return value of the failed | |||||
action. | |||||
filename : The name of the file or directory that caused the | |||||
build error. Set to None if no files are associated with | |||||
this error. This might be different from the target | |||||
being built. For example, failure to create the | |||||
directory in which the target file will appear. It | |||||
can be None if the error is not due to a particular | |||||
filename. | |||||
exc_info : Info about exception that caused the build | |||||
error. Set to (None, None, None) if this build | |||||
error is not due to an exception. | |||||
Information about the cause of the location of the error: | |||||
--------------------------------------------------------- | |||||
node : the error occured while building this target node(s) | |||||
executor : the executor that caused the build to fail (might | |||||
be None if the build failures is not due to the | |||||
executor failing) | |||||
action : the action that caused the build to fail (might be | |||||
None if the build failures is not due to the an | |||||
action failure) | |||||
command : the command line for the action that caused the | |||||
build to fail (might be None if the build failures | |||||
is not due to the an action failure) | |||||
""" | |||||
def __init__(self, | |||||
node=None, errstr="Unknown error", status=2, exitstatus=2, | |||||
filename=None, executor=None, action=None, command=None, | |||||
exc_info=(None, None, None)): | |||||
# py3: errstr should be string and not bytes. | |||||
self.errstr = SCons.Util.to_str(errstr) | |||||
self.status = status | |||||
self.exitstatus = exitstatus | |||||
self.filename = filename | |||||
self.exc_info = exc_info | |||||
self.node = node | |||||
self.executor = executor | |||||
self.action = action | |||||
self.command = command | |||||
Exception.__init__(self, node, errstr, status, exitstatus, filename, | |||||
executor, action, command, exc_info) | |||||
def __str__(self): | |||||
if self.filename: | |||||
return self.filename + ': ' + self.errstr | |||||
else: | |||||
return self.errstr | |||||
class InternalError(Exception): | |||||
pass | |||||
class UserError(Exception): | |||||
pass | |||||
class StopError(Exception): | |||||
pass | |||||
class EnvironmentError(Exception): | |||||
pass | |||||
class MSVCError(IOError): | |||||
pass | |||||
class ExplicitExit(Exception): | |||||
def __init__(self, node=None, status=None, *args): | |||||
self.node = node | |||||
self.status = status | |||||
self.exitstatus = status | |||||
Exception.__init__(self, *args) | |||||
def convert_to_BuildError(status, exc_info=None): | |||||
""" | |||||
Convert any return code a BuildError Exception. | |||||
:Parameters: | |||||
- `status`: can either be a return code or an Exception. | |||||
The buildError.status we set here will normally be | |||||
used as the exit status of the "scons" process. | |||||
""" | |||||
if not exc_info and isinstance(status, Exception): | |||||
exc_info = (status.__class__, status, None) | |||||
if isinstance(status, BuildError): | |||||
buildError = status | |||||
buildError.exitstatus = 2 # always exit with 2 on build errors | |||||
elif isinstance(status, ExplicitExit): | |||||
status = status.status | |||||
errstr = 'Explicit exit, status %s' % status | |||||
buildError = BuildError( | |||||
errstr=errstr, | |||||
status=status, # might be 0, OK here | |||||
exitstatus=status, # might be 0, OK here | |||||
exc_info=exc_info) | |||||
elif isinstance(status, (StopError, UserError)): | |||||
buildError = BuildError( | |||||
errstr=str(status), | |||||
status=2, | |||||
exitstatus=2, | |||||
exc_info=exc_info) | |||||
elif isinstance(status, shutil.SameFileError): | |||||
# PY3 has a exception for when copying file to itself | |||||
# It's object provides info differently than below | |||||
try: | |||||
filename = status.filename | |||||
except AttributeError: | |||||
filename = None | |||||
buildError = BuildError( | |||||
errstr=status.args[0], | |||||
status=status.errno, | |||||
exitstatus=2, | |||||
filename=filename, | |||||
exc_info=exc_info) | |||||
elif isinstance(status, (EnvironmentError, OSError, IOError)): | |||||
# If an IOError/OSError happens, raise a BuildError. | |||||
# Report the name of the file or directory that caused the | |||||
# error, which might be different from the target being built | |||||
# (for example, failure to create the directory in which the | |||||
# target file will appear). | |||||
try: | |||||
filename = status.filename | |||||
except AttributeError: | |||||
filename = None | |||||
buildError = BuildError( | |||||
errstr=status.strerror, | |||||
status=status.errno, | |||||
exitstatus=2, | |||||
filename=filename, | |||||
exc_info=exc_info) | |||||
elif isinstance(status, Exception): | |||||
buildError = BuildError( | |||||
errstr='%s : %s' % (status.__class__.__name__, status), | |||||
status=2, | |||||
exitstatus=2, | |||||
exc_info=exc_info) | |||||
elif SCons.Util.is_String(status): | |||||
buildError = BuildError( | |||||
errstr=status, | |||||
status=2, | |||||
exitstatus=2) | |||||
else: | |||||
buildError = BuildError( | |||||
errstr="Error %s" % status, | |||||
status=status, | |||||
exitstatus=2) | |||||
#import sys | |||||
#sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)\n"%(status,buildError.errstr, buildError.status)) | |||||
return buildError | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,670 @@ | |||||
"""SCons.Executor | |||||
A module for executing actions with specific lists of target and source | |||||
Nodes. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
from __future__ import print_function | |||||
__revision__ = "src/engine/SCons/Executor.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import collections | |||||
import SCons.Debug | |||||
from SCons.Debug import logInstanceCreation | |||||
import SCons.Errors | |||||
import SCons.Memoize | |||||
from SCons.compat import with_metaclass, NoSlotsPyPy | |||||
class Batch(object): | |||||
"""Remembers exact association between targets | |||||
and sources of executor.""" | |||||
__slots__ = ('targets', | |||||
'sources') | |||||
def __init__(self, targets=[], sources=[]): | |||||
self.targets = targets | |||||
self.sources = sources | |||||
class TSList(collections.UserList): | |||||
"""A class that implements $TARGETS or $SOURCES expansions by wrapping | |||||
an executor Method. This class is used in the Executor.lvars() | |||||
to delay creation of NodeList objects until they're needed. | |||||
Note that we subclass collections.UserList purely so that the | |||||
is_Sequence() function will identify an object of this class as | |||||
a list during variable expansion. We're not really using any | |||||
collections.UserList methods in practice. | |||||
""" | |||||
def __init__(self, func): | |||||
self.func = func | |||||
def __getattr__(self, attr): | |||||
nl = self.func() | |||||
return getattr(nl, attr) | |||||
def __getitem__(self, i): | |||||
nl = self.func() | |||||
return nl[i] | |||||
def __getslice__(self, i, j): | |||||
nl = self.func() | |||||
i = max(i, 0); j = max(j, 0) | |||||
return nl[i:j] | |||||
def __str__(self): | |||||
nl = self.func() | |||||
return str(nl) | |||||
def __repr__(self): | |||||
nl = self.func() | |||||
return repr(nl) | |||||
class TSObject(object): | |||||
"""A class that implements $TARGET or $SOURCE expansions by wrapping | |||||
an Executor method. | |||||
""" | |||||
def __init__(self, func): | |||||
self.func = func | |||||
def __getattr__(self, attr): | |||||
n = self.func() | |||||
return getattr(n, attr) | |||||
def __str__(self): | |||||
n = self.func() | |||||
if n: | |||||
return str(n) | |||||
return '' | |||||
def __repr__(self): | |||||
n = self.func() | |||||
if n: | |||||
return repr(n) | |||||
return '' | |||||
def rfile(node): | |||||
""" | |||||
A function to return the results of a Node's rfile() method, | |||||
if it exists, and the Node itself otherwise (if it's a Value | |||||
Node, e.g.). | |||||
""" | |||||
try: | |||||
rfile = node.rfile | |||||
except AttributeError: | |||||
return node | |||||
else: | |||||
return rfile() | |||||
def execute_nothing(obj, target, kw): | |||||
return 0 | |||||
def execute_action_list(obj, target, kw): | |||||
"""Actually execute the action list.""" | |||||
env = obj.get_build_env() | |||||
kw = obj.get_kw(kw) | |||||
status = 0 | |||||
for act in obj.get_action_list(): | |||||
args = ([], [], env) | |||||
status = act(*args, **kw) | |||||
if isinstance(status, SCons.Errors.BuildError): | |||||
status.executor = obj | |||||
raise status | |||||
elif status: | |||||
msg = "Error %s" % status | |||||
raise SCons.Errors.BuildError( | |||||
errstr=msg, | |||||
node=obj.batches[0].targets, | |||||
executor=obj, | |||||
action=act) | |||||
return status | |||||
_do_execute_map = {0 : execute_nothing, | |||||
1 : execute_action_list} | |||||
def execute_actions_str(obj): | |||||
env = obj.get_build_env() | |||||
return "\n".join([action.genstring(obj.get_all_targets(), | |||||
obj.get_all_sources(), | |||||
env) | |||||
for action in obj.get_action_list()]) | |||||
def execute_null_str(obj): | |||||
return '' | |||||
_execute_str_map = {0 : execute_null_str, | |||||
1 : execute_actions_str} | |||||
class Executor(object, with_metaclass(NoSlotsPyPy)): | |||||
"""A class for controlling instances of executing an action. | |||||
This largely exists to hold a single association of an action, | |||||
environment, list of environment override dictionaries, targets | |||||
and sources for later processing as needed. | |||||
""" | |||||
__slots__ = ('pre_actions', | |||||
'post_actions', | |||||
'env', | |||||
'overridelist', | |||||
'batches', | |||||
'builder_kw', | |||||
'_memo', | |||||
'lvars', | |||||
'_changed_sources_list', | |||||
'_changed_targets_list', | |||||
'_unchanged_sources_list', | |||||
'_unchanged_targets_list', | |||||
'action_list', | |||||
'_do_execute', | |||||
'_execute_str') | |||||
def __init__(self, action, env=None, overridelist=[{}], | |||||
targets=[], sources=[], builder_kw={}): | |||||
if SCons.Debug.track_instances: logInstanceCreation(self, 'Executor.Executor') | |||||
self.set_action_list(action) | |||||
self.pre_actions = [] | |||||
self.post_actions = [] | |||||
self.env = env | |||||
self.overridelist = overridelist | |||||
if targets or sources: | |||||
self.batches = [Batch(targets[:], sources[:])] | |||||
else: | |||||
self.batches = [] | |||||
self.builder_kw = builder_kw | |||||
self._do_execute = 1 | |||||
self._execute_str = 1 | |||||
self._memo = {} | |||||
def get_lvars(self): | |||||
try: | |||||
return self.lvars | |||||
except AttributeError: | |||||
self.lvars = { | |||||
'CHANGED_SOURCES' : TSList(self._get_changed_sources), | |||||
'CHANGED_TARGETS' : TSList(self._get_changed_targets), | |||||
'SOURCE' : TSObject(self._get_source), | |||||
'SOURCES' : TSList(self._get_sources), | |||||
'TARGET' : TSObject(self._get_target), | |||||
'TARGETS' : TSList(self._get_targets), | |||||
'UNCHANGED_SOURCES' : TSList(self._get_unchanged_sources), | |||||
'UNCHANGED_TARGETS' : TSList(self._get_unchanged_targets), | |||||
} | |||||
return self.lvars | |||||
def _get_changes(self): | |||||
cs = [] | |||||
ct = [] | |||||
us = [] | |||||
ut = [] | |||||
for b in self.batches: | |||||
# don't add targets marked always build to unchanged lists | |||||
# add to changed list as they always need to build | |||||
if not b.targets[0].always_build and b.targets[0].is_up_to_date(): | |||||
us.extend(list(map(rfile, b.sources))) | |||||
ut.extend(b.targets) | |||||
else: | |||||
cs.extend(list(map(rfile, b.sources))) | |||||
ct.extend(b.targets) | |||||
self._changed_sources_list = SCons.Util.NodeList(cs) | |||||
self._changed_targets_list = SCons.Util.NodeList(ct) | |||||
self._unchanged_sources_list = SCons.Util.NodeList(us) | |||||
self._unchanged_targets_list = SCons.Util.NodeList(ut) | |||||
def _get_changed_sources(self, *args, **kw): | |||||
try: | |||||
return self._changed_sources_list | |||||
except AttributeError: | |||||
self._get_changes() | |||||
return self._changed_sources_list | |||||
def _get_changed_targets(self, *args, **kw): | |||||
try: | |||||
return self._changed_targets_list | |||||
except AttributeError: | |||||
self._get_changes() | |||||
return self._changed_targets_list | |||||
def _get_source(self, *args, **kw): | |||||
return rfile(self.batches[0].sources[0]).get_subst_proxy() | |||||
def _get_sources(self, *args, **kw): | |||||
return SCons.Util.NodeList([rfile(n).get_subst_proxy() for n in self.get_all_sources()]) | |||||
def _get_target(self, *args, **kw): | |||||
return self.batches[0].targets[0].get_subst_proxy() | |||||
def _get_targets(self, *args, **kw): | |||||
return SCons.Util.NodeList([n.get_subst_proxy() for n in self.get_all_targets()]) | |||||
def _get_unchanged_sources(self, *args, **kw): | |||||
try: | |||||
return self._unchanged_sources_list | |||||
except AttributeError: | |||||
self._get_changes() | |||||
return self._unchanged_sources_list | |||||
def _get_unchanged_targets(self, *args, **kw): | |||||
try: | |||||
return self._unchanged_targets_list | |||||
except AttributeError: | |||||
self._get_changes() | |||||
return self._unchanged_targets_list | |||||
def get_action_targets(self): | |||||
if not self.action_list: | |||||
return [] | |||||
targets_string = self.action_list[0].get_targets(self.env, self) | |||||
if targets_string[0] == '$': | |||||
targets_string = targets_string[1:] | |||||
return self.get_lvars()[targets_string] | |||||
def set_action_list(self, action): | |||||
import SCons.Util | |||||
if not SCons.Util.is_List(action): | |||||
if not action: | |||||
import SCons.Errors | |||||
raise SCons.Errors.UserError("Executor must have an action.") | |||||
action = [action] | |||||
self.action_list = action | |||||
def get_action_list(self): | |||||
if self.action_list is None: | |||||
return [] | |||||
return self.pre_actions + self.action_list + self.post_actions | |||||
def get_all_targets(self): | |||||
"""Returns all targets for all batches of this Executor.""" | |||||
result = [] | |||||
for batch in self.batches: | |||||
result.extend(batch.targets) | |||||
return result | |||||
def get_all_sources(self): | |||||
"""Returns all sources for all batches of this Executor.""" | |||||
result = [] | |||||
for batch in self.batches: | |||||
result.extend(batch.sources) | |||||
return result | |||||
def get_all_children(self): | |||||
"""Returns all unique children (dependencies) for all batches | |||||
of this Executor. | |||||
The Taskmaster can recognize when it's already evaluated a | |||||
Node, so we don't have to make this list unique for its intended | |||||
canonical use case, but we expect there to be a lot of redundancy | |||||
(long lists of batched .cc files #including the same .h files | |||||
over and over), so removing the duplicates once up front should | |||||
save the Taskmaster a lot of work. | |||||
""" | |||||
result = SCons.Util.UniqueList([]) | |||||
for target in self.get_all_targets(): | |||||
result.extend(target.children()) | |||||
return result | |||||
def get_all_prerequisites(self): | |||||
"""Returns all unique (order-only) prerequisites for all batches | |||||
of this Executor. | |||||
""" | |||||
result = SCons.Util.UniqueList([]) | |||||
for target in self.get_all_targets(): | |||||
if target.prerequisites is not None: | |||||
result.extend(target.prerequisites) | |||||
return result | |||||
def get_action_side_effects(self): | |||||
"""Returns all side effects for all batches of this | |||||
Executor used by the underlying Action. | |||||
""" | |||||
result = SCons.Util.UniqueList([]) | |||||
for target in self.get_action_targets(): | |||||
result.extend(target.side_effects) | |||||
return result | |||||
@SCons.Memoize.CountMethodCall | |||||
def get_build_env(self): | |||||
"""Fetch or create the appropriate build Environment | |||||
for this Executor. | |||||
""" | |||||
try: | |||||
return self._memo['get_build_env'] | |||||
except KeyError: | |||||
pass | |||||
# Create the build environment instance with appropriate | |||||
# overrides. These get evaluated against the current | |||||
# environment's construction variables so that users can | |||||
# add to existing values by referencing the variable in | |||||
# the expansion. | |||||
overrides = {} | |||||
for odict in self.overridelist: | |||||
overrides.update(odict) | |||||
import SCons.Defaults | |||||
env = self.env or SCons.Defaults.DefaultEnvironment() | |||||
build_env = env.Override(overrides) | |||||
self._memo['get_build_env'] = build_env | |||||
return build_env | |||||
def get_build_scanner_path(self, scanner): | |||||
"""Fetch the scanner path for this executor's targets and sources. | |||||
""" | |||||
env = self.get_build_env() | |||||
try: | |||||
cwd = self.batches[0].targets[0].cwd | |||||
except (IndexError, AttributeError): | |||||
cwd = None | |||||
return scanner.path(env, cwd, | |||||
self.get_all_targets(), | |||||
self.get_all_sources()) | |||||
def get_kw(self, kw={}): | |||||
result = self.builder_kw.copy() | |||||
result.update(kw) | |||||
result['executor'] = self | |||||
return result | |||||
# use extra indirection because with new-style objects (Python 2.2 | |||||
# and above) we can't override special methods, and nullify() needs | |||||
# to be able to do this. | |||||
def __call__(self, target, **kw): | |||||
return _do_execute_map[self._do_execute](self, target, kw) | |||||
def cleanup(self): | |||||
self._memo = {} | |||||
def add_sources(self, sources): | |||||
"""Add source files to this Executor's list. This is necessary | |||||
for "multi" Builders that can be called repeatedly to build up | |||||
a source file list for a given target.""" | |||||
# TODO(batch): extend to multiple batches | |||||
assert (len(self.batches) == 1) | |||||
# TODO(batch): remove duplicates? | |||||
sources = [x for x in sources if x not in self.batches[0].sources] | |||||
self.batches[0].sources.extend(sources) | |||||
def get_sources(self): | |||||
return self.batches[0].sources | |||||
def add_batch(self, targets, sources): | |||||
"""Add pair of associated target and source to this Executor's list. | |||||
This is necessary for "batch" Builders that can be called repeatedly | |||||
to build up a list of matching target and source files that will be | |||||
used in order to update multiple target files at once from multiple | |||||
corresponding source files, for tools like MSVC that support it.""" | |||||
self.batches.append(Batch(targets, sources)) | |||||
def prepare(self): | |||||
""" | |||||
Preparatory checks for whether this Executor can go ahead | |||||
and (try to) build its targets. | |||||
""" | |||||
for s in self.get_all_sources(): | |||||
if s.missing(): | |||||
msg = "Source `%s' not found, needed by target `%s'." | |||||
raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0])) | |||||
def add_pre_action(self, action): | |||||
self.pre_actions.append(action) | |||||
def add_post_action(self, action): | |||||
self.post_actions.append(action) | |||||
# another extra indirection for new-style objects and nullify... | |||||
def __str__(self): | |||||
return _execute_str_map[self._execute_str](self) | |||||
def nullify(self): | |||||
self.cleanup() | |||||
self._do_execute = 0 | |||||
self._execute_str = 0 | |||||
@SCons.Memoize.CountMethodCall | |||||
def get_contents(self): | |||||
"""Fetch the signature contents. This is the main reason this | |||||
class exists, so we can compute this once and cache it regardless | |||||
of how many target or source Nodes there are. | |||||
""" | |||||
try: | |||||
return self._memo['get_contents'] | |||||
except KeyError: | |||||
pass | |||||
env = self.get_build_env() | |||||
action_list = self.get_action_list() | |||||
all_targets = self.get_all_targets() | |||||
all_sources = self.get_all_sources() | |||||
result = bytearray("",'utf-8').join([action.get_contents(all_targets, | |||||
all_sources, | |||||
env) | |||||
for action in action_list]) | |||||
self._memo['get_contents'] = result | |||||
return result | |||||
def get_timestamp(self): | |||||
"""Fetch a time stamp for this Executor. We don't have one, of | |||||
course (only files do), but this is the interface used by the | |||||
timestamp module. | |||||
""" | |||||
return 0 | |||||
def scan_targets(self, scanner): | |||||
# TODO(batch): scan by batches | |||||
self.scan(scanner, self.get_all_targets()) | |||||
def scan_sources(self, scanner): | |||||
# TODO(batch): scan by batches | |||||
if self.batches[0].sources: | |||||
self.scan(scanner, self.get_all_sources()) | |||||
def scan(self, scanner, node_list): | |||||
"""Scan a list of this Executor's files (targets or sources) for | |||||
implicit dependencies and update all of the targets with them. | |||||
This essentially short-circuits an N*M scan of the sources for | |||||
each individual target, which is a hell of a lot more efficient. | |||||
""" | |||||
env = self.get_build_env() | |||||
path = self.get_build_scanner_path | |||||
kw = self.get_kw() | |||||
# TODO(batch): scan by batches) | |||||
deps = [] | |||||
for node in node_list: | |||||
node.disambiguate() | |||||
deps.extend(node.get_implicit_deps(env, scanner, path, kw)) | |||||
deps.extend(self.get_implicit_deps()) | |||||
for tgt in self.get_all_targets(): | |||||
tgt.add_to_implicit(deps) | |||||
def _get_unignored_sources_key(self, node, ignore=()): | |||||
return (node,) + tuple(ignore) | |||||
@SCons.Memoize.CountDictCall(_get_unignored_sources_key) | |||||
def get_unignored_sources(self, node, ignore=()): | |||||
key = (node,) + tuple(ignore) | |||||
try: | |||||
memo_dict = self._memo['get_unignored_sources'] | |||||
except KeyError: | |||||
memo_dict = {} | |||||
self._memo['get_unignored_sources'] = memo_dict | |||||
else: | |||||
try: | |||||
return memo_dict[key] | |||||
except KeyError: | |||||
pass | |||||
if node: | |||||
# TODO: better way to do this (it's a linear search, | |||||
# but it may not be critical path)? | |||||
sourcelist = [] | |||||
for b in self.batches: | |||||
if node in b.targets: | |||||
sourcelist = b.sources | |||||
break | |||||
else: | |||||
sourcelist = self.get_all_sources() | |||||
if ignore: | |||||
idict = {} | |||||
for i in ignore: | |||||
idict[i] = 1 | |||||
sourcelist = [s for s in sourcelist if s not in idict] | |||||
memo_dict[key] = sourcelist | |||||
return sourcelist | |||||
def get_implicit_deps(self): | |||||
"""Return the executor's implicit dependencies, i.e. the nodes of | |||||
the commands to be executed.""" | |||||
result = [] | |||||
build_env = self.get_build_env() | |||||
for act in self.get_action_list(): | |||||
deps = act.get_implicit_deps(self.get_all_targets(), | |||||
self.get_all_sources(), | |||||
build_env) | |||||
result.extend(deps) | |||||
return result | |||||
_batch_executors = {} | |||||
def GetBatchExecutor(key): | |||||
return _batch_executors[key] | |||||
def AddBatchExecutor(key, executor): | |||||
assert key not in _batch_executors | |||||
_batch_executors[key] = executor | |||||
nullenv = None | |||||
import SCons.Util | |||||
class NullEnvironment(SCons.Util.Null): | |||||
import SCons.CacheDir | |||||
_CacheDir_path = None | |||||
_CacheDir = SCons.CacheDir.CacheDir(None) | |||||
def get_CacheDir(self): | |||||
return self._CacheDir | |||||
def get_NullEnvironment(): | |||||
"""Use singleton pattern for Null Environments.""" | |||||
global nullenv | |||||
if nullenv is None: | |||||
nullenv = NullEnvironment() | |||||
return nullenv | |||||
class Null(object, with_metaclass(NoSlotsPyPy)): | |||||
"""A null Executor, with a null build Environment, that does | |||||
nothing when the rest of the methods call it. | |||||
This might be able to disappear when we refactor things to | |||||
disassociate Builders from Nodes entirely, so we're not | |||||
going to worry about unit tests for this--at least for now. | |||||
""" | |||||
__slots__ = ('pre_actions', | |||||
'post_actions', | |||||
'env', | |||||
'overridelist', | |||||
'batches', | |||||
'builder_kw', | |||||
'_memo', | |||||
'lvars', | |||||
'_changed_sources_list', | |||||
'_changed_targets_list', | |||||
'_unchanged_sources_list', | |||||
'_unchanged_targets_list', | |||||
'action_list', | |||||
'_do_execute', | |||||
'_execute_str') | |||||
def __init__(self, *args, **kw): | |||||
if SCons.Debug.track_instances: logInstanceCreation(self, 'Executor.Null') | |||||
self.batches = [Batch(kw['targets'][:], [])] | |||||
def get_build_env(self): | |||||
return get_NullEnvironment() | |||||
def get_build_scanner_path(self): | |||||
return None | |||||
def cleanup(self): | |||||
pass | |||||
def prepare(self): | |||||
pass | |||||
def get_unignored_sources(self, *args, **kw): | |||||
return tuple(()) | |||||
def get_action_targets(self): | |||||
return [] | |||||
def get_action_list(self): | |||||
return [] | |||||
def get_all_targets(self): | |||||
return self.batches[0].targets | |||||
def get_all_sources(self): | |||||
return self.batches[0].targets[0].sources | |||||
def get_all_children(self): | |||||
return self.batches[0].targets[0].children() | |||||
def get_all_prerequisites(self): | |||||
return [] | |||||
def get_action_side_effects(self): | |||||
return [] | |||||
def __call__(self, *args, **kw): | |||||
return 0 | |||||
def get_contents(self): | |||||
return '' | |||||
def _morph(self): | |||||
"""Morph this Null executor to a real Executor object.""" | |||||
batches = self.batches | |||||
self.__class__ = Executor | |||||
self.__init__([]) | |||||
self.batches = batches | |||||
# The following methods require morphing this Null Executor to a | |||||
# real Executor object. | |||||
def add_pre_action(self, action): | |||||
self._morph() | |||||
self.add_pre_action(action) | |||||
def add_post_action(self, action): | |||||
self._morph() | |||||
self.add_post_action(action) | |||||
def set_action_list(self, action): | |||||
self._morph() | |||||
self.set_action_list(action) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,435 @@ | |||||
"""SCons.Job | |||||
This module defines the Serial and Parallel classes that execute tasks to | |||||
complete a build. The Jobs class provides a higher level interface to start, | |||||
stop, and wait on jobs. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Job.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.compat | |||||
import os | |||||
import signal | |||||
import SCons.Errors | |||||
# The default stack size (in kilobytes) of the threads used to execute | |||||
# jobs in parallel. | |||||
# | |||||
# We use a stack size of 256 kilobytes. The default on some platforms | |||||
# is too large and prevents us from creating enough threads to fully | |||||
# parallelized the build. For example, the default stack size on linux | |||||
# is 8 MBytes. | |||||
explicit_stack_size = None | |||||
default_stack_size = 256 | |||||
interrupt_msg = 'Build interrupted.' | |||||
class InterruptState(object): | |||||
def __init__(self): | |||||
self.interrupted = False | |||||
def set(self): | |||||
self.interrupted = True | |||||
def __call__(self): | |||||
return self.interrupted | |||||
class Jobs(object): | |||||
"""An instance of this class initializes N jobs, and provides | |||||
methods for starting, stopping, and waiting on all N jobs. | |||||
""" | |||||
def __init__(self, num, taskmaster): | |||||
""" | |||||
Create 'num' jobs using the given taskmaster. | |||||
If 'num' is 1 or less, then a serial job will be used, | |||||
otherwise a parallel job with 'num' worker threads will | |||||
be used. | |||||
The 'num_jobs' attribute will be set to the actual number of jobs | |||||
allocated. If more than one job is requested but the Parallel | |||||
class can't do it, it gets reset to 1. Wrapping interfaces that | |||||
care should check the value of 'num_jobs' after initialization. | |||||
""" | |||||
self.job = None | |||||
if num > 1: | |||||
stack_size = explicit_stack_size | |||||
if stack_size is None: | |||||
stack_size = default_stack_size | |||||
try: | |||||
self.job = Parallel(taskmaster, num, stack_size) | |||||
self.num_jobs = num | |||||
except NameError: | |||||
pass | |||||
if self.job is None: | |||||
self.job = Serial(taskmaster) | |||||
self.num_jobs = 1 | |||||
def run(self, postfunc=lambda: None): | |||||
"""Run the jobs. | |||||
postfunc() will be invoked after the jobs has run. It will be | |||||
invoked even if the jobs are interrupted by a keyboard | |||||
interrupt (well, in fact by a signal such as either SIGINT, | |||||
SIGTERM or SIGHUP). The execution of postfunc() is protected | |||||
against keyboard interrupts and is guaranteed to run to | |||||
completion.""" | |||||
self._setup_sig_handler() | |||||
try: | |||||
self.job.start() | |||||
finally: | |||||
postfunc() | |||||
self._reset_sig_handler() | |||||
def were_interrupted(self): | |||||
"""Returns whether the jobs were interrupted by a signal.""" | |||||
return self.job.interrupted() | |||||
def _setup_sig_handler(self): | |||||
"""Setup an interrupt handler so that SCons can shutdown cleanly in | |||||
various conditions: | |||||
a) SIGINT: Keyboard interrupt | |||||
b) SIGTERM: kill or system shutdown | |||||
c) SIGHUP: Controlling shell exiting | |||||
We handle all of these cases by stopping the taskmaster. It | |||||
turns out that it's very difficult to stop the build process | |||||
by throwing asynchronously an exception such as | |||||
KeyboardInterrupt. For example, the python Condition | |||||
variables (threading.Condition) and queues do not seem to be | |||||
asynchronous-exception-safe. It would require adding a whole | |||||
bunch of try/finally block and except KeyboardInterrupt all | |||||
over the place. | |||||
Note also that we have to be careful to handle the case when | |||||
SCons forks before executing another process. In that case, we | |||||
want the child to exit immediately. | |||||
""" | |||||
def handler(signum, stack, self=self, parentpid=os.getpid()): | |||||
if os.getpid() == parentpid: | |||||
self.job.taskmaster.stop() | |||||
self.job.interrupted.set() | |||||
else: | |||||
os._exit(2) | |||||
self.old_sigint = signal.signal(signal.SIGINT, handler) | |||||
self.old_sigterm = signal.signal(signal.SIGTERM, handler) | |||||
try: | |||||
self.old_sighup = signal.signal(signal.SIGHUP, handler) | |||||
except AttributeError: | |||||
pass | |||||
def _reset_sig_handler(self): | |||||
"""Restore the signal handlers to their previous state (before the | |||||
call to _setup_sig_handler().""" | |||||
signal.signal(signal.SIGINT, self.old_sigint) | |||||
signal.signal(signal.SIGTERM, self.old_sigterm) | |||||
try: | |||||
signal.signal(signal.SIGHUP, self.old_sighup) | |||||
except AttributeError: | |||||
pass | |||||
class Serial(object): | |||||
"""This class is used to execute tasks in series, and is more efficient | |||||
than Parallel, but is only appropriate for non-parallel builds. Only | |||||
one instance of this class should be in existence at a time. | |||||
This class is not thread safe. | |||||
""" | |||||
def __init__(self, taskmaster): | |||||
"""Create a new serial job given a taskmaster. | |||||
The taskmaster's next_task() method should return the next task | |||||
that needs to be executed, or None if there are no more tasks. The | |||||
taskmaster's executed() method will be called for each task when it | |||||
is successfully executed, or failed() will be called if it failed to | |||||
execute (e.g. execute() raised an exception).""" | |||||
self.taskmaster = taskmaster | |||||
self.interrupted = InterruptState() | |||||
def start(self): | |||||
"""Start the job. This will begin pulling tasks from the taskmaster | |||||
and executing them, and return when there are no more tasks. If a task | |||||
fails to execute (i.e. execute() raises an exception), then the job will | |||||
stop.""" | |||||
while True: | |||||
task = self.taskmaster.next_task() | |||||
if task is None: | |||||
break | |||||
try: | |||||
task.prepare() | |||||
if task.needs_execute(): | |||||
task.execute() | |||||
except: | |||||
if self.interrupted(): | |||||
try: | |||||
raise SCons.Errors.BuildError( | |||||
task.targets[0], errstr=interrupt_msg) | |||||
except: | |||||
task.exception_set() | |||||
else: | |||||
task.exception_set() | |||||
# Let the failed() callback function arrange for the | |||||
# build to stop if that's appropriate. | |||||
task.failed() | |||||
else: | |||||
task.executed() | |||||
task.postprocess() | |||||
self.taskmaster.cleanup() | |||||
# Trap import failure so that everything in the Job module but the | |||||
# Parallel class (and its dependent classes) will work if the interpreter | |||||
# doesn't support threads. | |||||
try: | |||||
import queue | |||||
import threading | |||||
except ImportError: | |||||
pass | |||||
else: | |||||
class Worker(threading.Thread): | |||||
"""A worker thread waits on a task to be posted to its request queue, | |||||
dequeues the task, executes it, and posts a tuple including the task | |||||
and a boolean indicating whether the task executed successfully. """ | |||||
def __init__(self, requestQueue, resultsQueue, interrupted): | |||||
threading.Thread.__init__(self) | |||||
self.setDaemon(1) | |||||
self.requestQueue = requestQueue | |||||
self.resultsQueue = resultsQueue | |||||
self.interrupted = interrupted | |||||
self.start() | |||||
def run(self): | |||||
while True: | |||||
task = self.requestQueue.get() | |||||
if task is None: | |||||
# The "None" value is used as a sentinel by | |||||
# ThreadPool.cleanup(). This indicates that there | |||||
# are no more tasks, so we should quit. | |||||
break | |||||
try: | |||||
if self.interrupted(): | |||||
raise SCons.Errors.BuildError( | |||||
task.targets[0], errstr=interrupt_msg) | |||||
task.execute() | |||||
except: | |||||
task.exception_set() | |||||
ok = False | |||||
else: | |||||
ok = True | |||||
self.resultsQueue.put((task, ok)) | |||||
class ThreadPool(object): | |||||
"""This class is responsible for spawning and managing worker threads.""" | |||||
def __init__(self, num, stack_size, interrupted): | |||||
"""Create the request and reply queues, and 'num' worker threads. | |||||
One must specify the stack size of the worker threads. The | |||||
stack size is specified in kilobytes. | |||||
""" | |||||
self.requestQueue = queue.Queue(0) | |||||
self.resultsQueue = queue.Queue(0) | |||||
try: | |||||
prev_size = threading.stack_size(stack_size*1024) | |||||
except AttributeError as e: | |||||
# Only print a warning if the stack size has been | |||||
# explicitly set. | |||||
if not explicit_stack_size is None: | |||||
msg = "Setting stack size is unsupported by this version of Python:\n " + \ | |||||
e.args[0] | |||||
SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) | |||||
except ValueError as e: | |||||
msg = "Setting stack size failed:\n " + str(e) | |||||
SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) | |||||
# Create worker threads | |||||
self.workers = [] | |||||
for _ in range(num): | |||||
worker = Worker(self.requestQueue, self.resultsQueue, interrupted) | |||||
self.workers.append(worker) | |||||
if 'prev_size' in locals(): | |||||
threading.stack_size(prev_size) | |||||
def put(self, task): | |||||
"""Put task into request queue.""" | |||||
self.requestQueue.put(task) | |||||
def get(self): | |||||
"""Remove and return a result tuple from the results queue.""" | |||||
return self.resultsQueue.get() | |||||
def preparation_failed(self, task): | |||||
self.resultsQueue.put((task, False)) | |||||
def cleanup(self): | |||||
""" | |||||
Shuts down the thread pool, giving each worker thread a | |||||
chance to shut down gracefully. | |||||
""" | |||||
# For each worker thread, put a sentinel "None" value | |||||
# on the requestQueue (indicating that there's no work | |||||
# to be done) so that each worker thread will get one and | |||||
# terminate gracefully. | |||||
for _ in self.workers: | |||||
self.requestQueue.put(None) | |||||
# Wait for all of the workers to terminate. | |||||
# | |||||
# If we don't do this, later Python versions (2.4, 2.5) often | |||||
# seem to raise exceptions during shutdown. This happens | |||||
# in requestQueue.get(), as an assertion failure that | |||||
# requestQueue.not_full is notified while not acquired, | |||||
# seemingly because the main thread has shut down (or is | |||||
# in the process of doing so) while the workers are still | |||||
# trying to pull sentinels off the requestQueue. | |||||
# | |||||
# Normally these terminations should happen fairly quickly, | |||||
# but we'll stick a one-second timeout on here just in case | |||||
# someone gets hung. | |||||
for worker in self.workers: | |||||
worker.join(1.0) | |||||
self.workers = [] | |||||
class Parallel(object): | |||||
"""This class is used to execute tasks in parallel, and is somewhat | |||||
less efficient than Serial, but is appropriate for parallel builds. | |||||
This class is thread safe. | |||||
""" | |||||
def __init__(self, taskmaster, num, stack_size): | |||||
"""Create a new parallel job given a taskmaster. | |||||
The taskmaster's next_task() method should return the next | |||||
task that needs to be executed, or None if there are no more | |||||
tasks. The taskmaster's executed() method will be called | |||||
for each task when it is successfully executed, or failed() | |||||
will be called if the task failed to execute (i.e. execute() | |||||
raised an exception). | |||||
Note: calls to taskmaster are serialized, but calls to | |||||
execute() on distinct tasks are not serialized, because | |||||
that is the whole point of parallel jobs: they can execute | |||||
multiple tasks simultaneously. """ | |||||
self.taskmaster = taskmaster | |||||
self.interrupted = InterruptState() | |||||
self.tp = ThreadPool(num, stack_size, self.interrupted) | |||||
self.maxjobs = num | |||||
def start(self): | |||||
"""Start the job. This will begin pulling tasks from the | |||||
taskmaster and executing them, and return when there are no | |||||
more tasks. If a task fails to execute (i.e. execute() raises | |||||
an exception), then the job will stop.""" | |||||
jobs = 0 | |||||
while True: | |||||
# Start up as many available tasks as we're | |||||
# allowed to. | |||||
while jobs < self.maxjobs: | |||||
task = self.taskmaster.next_task() | |||||
if task is None: | |||||
break | |||||
try: | |||||
# prepare task for execution | |||||
task.prepare() | |||||
except: | |||||
task.exception_set() | |||||
task.failed() | |||||
task.postprocess() | |||||
else: | |||||
if task.needs_execute(): | |||||
# dispatch task | |||||
self.tp.put(task) | |||||
jobs = jobs + 1 | |||||
else: | |||||
task.executed() | |||||
task.postprocess() | |||||
if not task and not jobs: break | |||||
# Let any/all completed tasks finish up before we go | |||||
# back and put the next batch of tasks on the queue. | |||||
while True: | |||||
task, ok = self.tp.get() | |||||
jobs = jobs - 1 | |||||
if ok: | |||||
task.executed() | |||||
else: | |||||
if self.interrupted(): | |||||
try: | |||||
raise SCons.Errors.BuildError( | |||||
task.targets[0], errstr=interrupt_msg) | |||||
except: | |||||
task.exception_set() | |||||
# Let the failed() callback function arrange | |||||
# for the build to stop if that's appropriate. | |||||
task.failed() | |||||
task.postprocess() | |||||
if self.tp.resultsQueue.empty(): | |||||
break | |||||
self.tp.cleanup() | |||||
self.taskmaster.cleanup() | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,245 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
from __future__ import print_function | |||||
__revision__ = "src/engine/SCons/Memoize.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Memoizer | |||||
A decorator-based implementation to count hits and misses of the computed | |||||
values that various methods cache in memory. | |||||
Use of this modules assumes that wrapped methods be coded to cache their | |||||
values in a consistent way. In particular, it requires that the class uses a | |||||
dictionary named "_memo" to store the cached values. | |||||
Here is an example of wrapping a method that returns a computed value, | |||||
with no input parameters:: | |||||
@SCons.Memoize.CountMethodCall | |||||
def foo(self): | |||||
try: # Memoization | |||||
return self._memo['foo'] # Memoization | |||||
except KeyError: # Memoization | |||||
pass # Memoization | |||||
result = self.compute_foo_value() | |||||
self._memo['foo'] = result # Memoization | |||||
return result | |||||
Here is an example of wrapping a method that will return different values | |||||
based on one or more input arguments:: | |||||
def _bar_key(self, argument): # Memoization | |||||
return argument # Memoization | |||||
@SCons.Memoize.CountDictCall(_bar_key) | |||||
def bar(self, argument): | |||||
memo_key = argument # Memoization | |||||
try: # Memoization | |||||
memo_dict = self._memo['bar'] # Memoization | |||||
except KeyError: # Memoization | |||||
memo_dict = {} # Memoization | |||||
self._memo['dict'] = memo_dict # Memoization | |||||
else: # Memoization | |||||
try: # Memoization | |||||
return memo_dict[memo_key] # Memoization | |||||
except KeyError: # Memoization | |||||
pass # Memoization | |||||
result = self.compute_bar_value(argument) | |||||
memo_dict[memo_key] = result # Memoization | |||||
return result | |||||
Deciding what to cache is tricky, because different configurations | |||||
can have radically different performance tradeoffs, and because the | |||||
tradeoffs involved are often so non-obvious. Consequently, deciding | |||||
whether or not to cache a given method will likely be more of an art than | |||||
a science, but should still be based on available data from this module. | |||||
Here are some VERY GENERAL guidelines about deciding whether or not to | |||||
cache return values from a method that's being called a lot: | |||||
-- The first question to ask is, "Can we change the calling code | |||||
so this method isn't called so often?" Sometimes this can be | |||||
done by changing the algorithm. Sometimes the *caller* should | |||||
be memoized, not the method you're looking at. | |||||
-- The memoized function should be timed with multiple configurations | |||||
to make sure it doesn't inadvertently slow down some other | |||||
configuration. | |||||
-- When memoizing values based on a dictionary key composed of | |||||
input arguments, you don't need to use all of the arguments | |||||
if some of them don't affect the return values. | |||||
""" | |||||
# A flag controlling whether or not we actually use memoization. | |||||
use_memoizer = None | |||||
# Global list of counter objects | |||||
CounterList = {} | |||||
class Counter(object): | |||||
""" | |||||
Base class for counting memoization hits and misses. | |||||
We expect that the initialization in a matching decorator will | |||||
fill in the correct class name and method name that represents | |||||
the name of the function being counted. | |||||
""" | |||||
def __init__(self, cls_name, method_name): | |||||
""" | |||||
""" | |||||
self.cls_name = cls_name | |||||
self.method_name = method_name | |||||
self.hit = 0 | |||||
self.miss = 0 | |||||
def key(self): | |||||
return self.cls_name+'.'+self.method_name | |||||
def display(self): | |||||
print(" {:7d} hits {:7d} misses {}()".format(self.hit, self.miss, self.key())) | |||||
def __eq__(self, other): | |||||
try: | |||||
return self.key() == other.key() | |||||
except AttributeError: | |||||
return True | |||||
class CountValue(Counter): | |||||
""" | |||||
A counter class for simple, atomic memoized values. | |||||
A CountValue object should be instantiated in a decorator for each of | |||||
the class's methods that memoizes its return value by simply storing | |||||
the return value in its _memo dictionary. | |||||
""" | |||||
def count(self, *args, **kw): | |||||
""" Counts whether the memoized value has already been | |||||
set (a hit) or not (a miss). | |||||
""" | |||||
obj = args[0] | |||||
if self.method_name in obj._memo: | |||||
self.hit = self.hit + 1 | |||||
else: | |||||
self.miss = self.miss + 1 | |||||
class CountDict(Counter): | |||||
""" | |||||
A counter class for memoized values stored in a dictionary, with | |||||
keys based on the method's input arguments. | |||||
A CountDict object is instantiated in a decorator for each of the | |||||
class's methods that memoizes its return value in a dictionary, | |||||
indexed by some key that can be computed from one or more of | |||||
its input arguments. | |||||
""" | |||||
def __init__(self, cls_name, method_name, keymaker): | |||||
""" | |||||
""" | |||||
Counter.__init__(self, cls_name, method_name) | |||||
self.keymaker = keymaker | |||||
def count(self, *args, **kw): | |||||
""" Counts whether the computed key value is already present | |||||
in the memoization dictionary (a hit) or not (a miss). | |||||
""" | |||||
obj = args[0] | |||||
try: | |||||
memo_dict = obj._memo[self.method_name] | |||||
except KeyError: | |||||
self.miss = self.miss + 1 | |||||
else: | |||||
key = self.keymaker(*args, **kw) | |||||
if key in memo_dict: | |||||
self.hit = self.hit + 1 | |||||
else: | |||||
self.miss = self.miss + 1 | |||||
def Dump(title=None): | |||||
""" Dump the hit/miss count for all the counters | |||||
collected so far. | |||||
""" | |||||
if title: | |||||
print(title) | |||||
for counter in sorted(CounterList): | |||||
CounterList[counter].display() | |||||
def EnableMemoization(): | |||||
global use_memoizer | |||||
use_memoizer = 1 | |||||
def CountMethodCall(fn): | |||||
""" Decorator for counting memoizer hits/misses while retrieving | |||||
a simple value in a class method. It wraps the given method | |||||
fn and uses a CountValue object to keep track of the | |||||
caching statistics. | |||||
Wrapping gets enabled by calling EnableMemoization(). | |||||
""" | |||||
if use_memoizer: | |||||
def wrapper(self, *args, **kwargs): | |||||
global CounterList | |||||
key = self.__class__.__name__+'.'+fn.__name__ | |||||
if key not in CounterList: | |||||
CounterList[key] = CountValue(self.__class__.__name__, fn.__name__) | |||||
CounterList[key].count(self, *args, **kwargs) | |||||
return fn(self, *args, **kwargs) | |||||
wrapper.__name__= fn.__name__ | |||||
return wrapper | |||||
else: | |||||
return fn | |||||
def CountDictCall(keyfunc): | |||||
""" Decorator for counting memoizer hits/misses while accessing | |||||
dictionary values with a key-generating function. Like | |||||
CountMethodCall above, it wraps the given method | |||||
fn and uses a CountDict object to keep track of the | |||||
caching statistics. The dict-key function keyfunc has to | |||||
get passed in the decorator call and gets stored in the | |||||
CountDict instance. | |||||
Wrapping gets enabled by calling EnableMemoization(). | |||||
""" | |||||
def decorator(fn): | |||||
if use_memoizer: | |||||
def wrapper(self, *args, **kwargs): | |||||
global CounterList | |||||
key = self.__class__.__name__+'.'+fn.__name__ | |||||
if key not in CounterList: | |||||
CounterList[key] = CountDict(self.__class__.__name__, fn.__name__, keyfunc) | |||||
CounterList[key].count(self, *args, **kwargs) | |||||
return fn(self, *args, **kwargs) | |||||
wrapper.__name__= fn.__name__ | |||||
return wrapper | |||||
else: | |||||
return fn | |||||
return decorator | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,181 @@ | |||||
"""scons.Node.Alias | |||||
Alias nodes. | |||||
This creates a hash of global Aliases (dummy targets). | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Node/Alias.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import collections | |||||
import SCons.Errors | |||||
import SCons.Node | |||||
import SCons.Util | |||||
class AliasNameSpace(collections.UserDict): | |||||
def Alias(self, name, **kw): | |||||
if isinstance(name, SCons.Node.Alias.Alias): | |||||
return name | |||||
try: | |||||
a = self[name] | |||||
except KeyError: | |||||
a = SCons.Node.Alias.Alias(name, **kw) | |||||
self[name] = a | |||||
return a | |||||
def lookup(self, name, **kw): | |||||
try: | |||||
return self[name] | |||||
except KeyError: | |||||
return None | |||||
class AliasNodeInfo(SCons.Node.NodeInfoBase): | |||||
__slots__ = ('csig',) | |||||
current_version_id = 2 | |||||
field_list = ['csig'] | |||||
def str_to_node(self, s): | |||||
return default_ans.Alias(s) | |||||
def __getstate__(self): | |||||
""" | |||||
Return all fields that shall be pickled. Walk the slots in the class | |||||
hierarchy and add those to the state dictionary. If a '__dict__' slot is | |||||
available, copy all entries to the dictionary. Also include the version | |||||
id, which is fixed for all instances of a class. | |||||
""" | |||||
state = getattr(self, '__dict__', {}).copy() | |||||
for obj in type(self).mro(): | |||||
for name in getattr(obj,'__slots__',()): | |||||
if hasattr(self, name): | |||||
state[name] = getattr(self, name) | |||||
state['_version_id'] = self.current_version_id | |||||
try: | |||||
del state['__weakref__'] | |||||
except KeyError: | |||||
pass | |||||
return state | |||||
def __setstate__(self, state): | |||||
""" | |||||
Restore the attributes from a pickled state. | |||||
""" | |||||
# TODO check or discard version | |||||
del state['_version_id'] | |||||
for key, value in state.items(): | |||||
if key not in ('__weakref__',): | |||||
setattr(self, key, value) | |||||
class AliasBuildInfo(SCons.Node.BuildInfoBase): | |||||
__slots__ = () | |||||
current_version_id = 2 | |||||
class Alias(SCons.Node.Node): | |||||
NodeInfo = AliasNodeInfo | |||||
BuildInfo = AliasBuildInfo | |||||
def __init__(self, name): | |||||
SCons.Node.Node.__init__(self) | |||||
self.name = name | |||||
self.changed_since_last_build = 1 | |||||
self.store_info = 0 | |||||
def str_for_display(self): | |||||
return '"' + self.__str__() + '"' | |||||
def __str__(self): | |||||
return self.name | |||||
def make_ready(self): | |||||
self.get_csig() | |||||
really_build = SCons.Node.Node.build | |||||
is_up_to_date = SCons.Node.Node.children_are_up_to_date | |||||
def is_under(self, dir): | |||||
# Make Alias nodes get built regardless of | |||||
# what directory scons was run from. Alias nodes | |||||
# are outside the filesystem: | |||||
return 1 | |||||
def get_contents(self): | |||||
"""The contents of an alias is the concatenation | |||||
of the content signatures of all its sources.""" | |||||
childsigs = [n.get_csig() for n in self.children()] | |||||
return ''.join(childsigs) | |||||
def sconsign(self): | |||||
"""An Alias is not recorded in .sconsign files""" | |||||
pass | |||||
# | |||||
# | |||||
# | |||||
def build(self): | |||||
"""A "builder" for aliases.""" | |||||
pass | |||||
def convert(self): | |||||
try: del self.builder | |||||
except AttributeError: pass | |||||
self.reset_executor() | |||||
self.build = self.really_build | |||||
def get_csig(self): | |||||
""" | |||||
Generate a node's content signature, the digested signature | |||||
of its content. | |||||
node - the node | |||||
cache - alternate node to use for the signature cache | |||||
returns - the content signature | |||||
""" | |||||
try: | |||||
return self.ninfo.csig | |||||
except AttributeError: | |||||
pass | |||||
contents = self.get_contents() | |||||
csig = SCons.Util.MD5signature(contents) | |||||
self.get_ninfo().csig = csig | |||||
return csig | |||||
default_ans = AliasNameSpace() | |||||
SCons.Node.arg2nodes_lookups.append(default_ans.lookup) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,171 @@ | |||||
"""scons.Node.Python | |||||
Python nodes. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Node/Python.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Node | |||||
class ValueNodeInfo(SCons.Node.NodeInfoBase): | |||||
__slots__ = ('csig',) | |||||
current_version_id = 2 | |||||
field_list = ['csig'] | |||||
def str_to_node(self, s): | |||||
return Value(s) | |||||
def __getstate__(self): | |||||
""" | |||||
Return all fields that shall be pickled. Walk the slots in the class | |||||
hierarchy and add those to the state dictionary. If a '__dict__' slot is | |||||
available, copy all entries to the dictionary. Also include the version | |||||
id, which is fixed for all instances of a class. | |||||
""" | |||||
state = getattr(self, '__dict__', {}).copy() | |||||
for obj in type(self).mro(): | |||||
for name in getattr(obj,'__slots__',()): | |||||
if hasattr(self, name): | |||||
state[name] = getattr(self, name) | |||||
state['_version_id'] = self.current_version_id | |||||
try: | |||||
del state['__weakref__'] | |||||
except KeyError: | |||||
pass | |||||
return state | |||||
def __setstate__(self, state): | |||||
""" | |||||
Restore the attributes from a pickled state. | |||||
""" | |||||
# TODO check or discard version | |||||
del state['_version_id'] | |||||
for key, value in state.items(): | |||||
if key not in ('__weakref__',): | |||||
setattr(self, key, value) | |||||
class ValueBuildInfo(SCons.Node.BuildInfoBase): | |||||
__slots__ = () | |||||
current_version_id = 2 | |||||
class Value(SCons.Node.Node): | |||||
"""A class for Python variables, typically passed on the command line | |||||
or generated by a script, but not from a file or some other source. | |||||
""" | |||||
NodeInfo = ValueNodeInfo | |||||
BuildInfo = ValueBuildInfo | |||||
def __init__(self, value, built_value=None): | |||||
SCons.Node.Node.__init__(self) | |||||
self.value = value | |||||
self.changed_since_last_build = 6 | |||||
self.store_info = 0 | |||||
if built_value is not None: | |||||
self.built_value = built_value | |||||
def str_for_display(self): | |||||
return repr(self.value) | |||||
def __str__(self): | |||||
return str(self.value) | |||||
def make_ready(self): | |||||
self.get_csig() | |||||
def build(self, **kw): | |||||
if not hasattr(self, 'built_value'): | |||||
SCons.Node.Node.build(self, **kw) | |||||
is_up_to_date = SCons.Node.Node.children_are_up_to_date | |||||
def is_under(self, dir): | |||||
# Make Value nodes get built regardless of | |||||
# what directory scons was run from. Value nodes | |||||
# are outside the filesystem: | |||||
return 1 | |||||
def write(self, built_value): | |||||
"""Set the value of the node.""" | |||||
self.built_value = built_value | |||||
def read(self): | |||||
"""Return the value. If necessary, the value is built.""" | |||||
self.build() | |||||
if not hasattr(self, 'built_value'): | |||||
self.built_value = self.value | |||||
return self.built_value | |||||
def get_text_contents(self): | |||||
"""By the assumption that the node.built_value is a | |||||
deterministic product of the sources, the contents of a Value | |||||
are the concatenation of all the contents of its sources. As | |||||
the value need not be built when get_contents() is called, we | |||||
cannot use the actual node.built_value.""" | |||||
###TODO: something reasonable about universal newlines | |||||
contents = str(self.value) | |||||
for kid in self.children(None): | |||||
contents = contents + kid.get_contents().decode() | |||||
return contents | |||||
def get_contents(self): | |||||
text_contents = self.get_text_contents() | |||||
try: | |||||
return text_contents.encode() | |||||
except UnicodeDecodeError: | |||||
# Already encoded as python2 str are bytes | |||||
return text_contents | |||||
def changed_since_last_build(self, target, prev_ni): | |||||
cur_csig = self.get_csig() | |||||
try: | |||||
return cur_csig != prev_ni.csig | |||||
except AttributeError: | |||||
return 1 | |||||
def get_csig(self, calc=None): | |||||
"""Because we're a Python value node and don't have a real | |||||
timestamp, we get to ignore the calculator and just use the | |||||
value contents.""" | |||||
try: | |||||
return self.ninfo.csig | |||||
except AttributeError: | |||||
pass | |||||
contents = self.get_contents() | |||||
self.get_ninfo().csig = contents | |||||
return contents | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,50 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Options/BoolOption.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Place-holder for the old SCons.Options module hierarchy | |||||
This is for backwards compatibility. The new equivalent is the Variables/ | |||||
class hierarchy. These will have deprecation warnings added (some day), | |||||
and will then be removed entirely (some day). | |||||
""" | |||||
import SCons.Variables | |||||
import SCons.Warnings | |||||
warned = False | |||||
def BoolOption(*args, **kw): | |||||
global warned | |||||
if not warned: | |||||
msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead." | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) | |||||
warned = True | |||||
return SCons.Variables.BoolVariable(*args, **kw) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,50 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Options/EnumOption.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Place-holder for the old SCons.Options module hierarchy | |||||
This is for backwards compatibility. The new equivalent is the Variables/ | |||||
class hierarchy. These will have deprecation warnings added (some day), | |||||
and will then be removed entirely (some day). | |||||
""" | |||||
import SCons.Variables | |||||
import SCons.Warnings | |||||
warned = False | |||||
def EnumOption(*args, **kw): | |||||
global warned | |||||
if not warned: | |||||
msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead." | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) | |||||
warned = True | |||||
return SCons.Variables.EnumVariable(*args, **kw) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,50 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Options/ListOption.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Place-holder for the old SCons.Options module hierarchy | |||||
This is for backwards compatibility. The new equivalent is the Variables/ | |||||
class hierarchy. These will have deprecation warnings added (some day), | |||||
and will then be removed entirely (some day). | |||||
""" | |||||
import SCons.Variables | |||||
import SCons.Warnings | |||||
warned = False | |||||
def ListOption(*args, **kw): | |||||
global warned | |||||
if not warned: | |||||
msg = "The ListOption() function is deprecated; use the ListVariable() function instead." | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) | |||||
warned = True | |||||
return SCons.Variables.ListVariable(*args, **kw) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,50 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Options/PackageOption.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Place-holder for the old SCons.Options module hierarchy | |||||
This is for backwards compatibility. The new equivalent is the Variables/ | |||||
class hierarchy. These will have deprecation warnings added (some day), | |||||
and will then be removed entirely (some day). | |||||
""" | |||||
import SCons.Variables | |||||
import SCons.Warnings | |||||
warned = False | |||||
def PackageOption(*args, **kw): | |||||
global warned | |||||
if not warned: | |||||
msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead." | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) | |||||
warned = True | |||||
return SCons.Variables.PackageVariable(*args, **kw) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,76 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Options/PathOption.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Place-holder for the old SCons.Options module hierarchy | |||||
This is for backwards compatibility. The new equivalent is the Variables/ | |||||
class hierarchy. These will have deprecation warnings added (some day), | |||||
and will then be removed entirely (some day). | |||||
""" | |||||
import SCons.Variables | |||||
import SCons.Warnings | |||||
warned = False | |||||
class _PathOptionClass(object): | |||||
def warn(self): | |||||
global warned | |||||
if not warned: | |||||
msg = "The PathOption() function is deprecated; use the PathVariable() function instead." | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) | |||||
warned = True | |||||
def __call__(self, *args, **kw): | |||||
self.warn() | |||||
return SCons.Variables.PathVariable(*args, **kw) | |||||
def PathAccept(self, *args, **kw): | |||||
self.warn() | |||||
return SCons.Variables.PathVariable.PathAccept(*args, **kw) | |||||
def PathIsDir(self, *args, **kw): | |||||
self.warn() | |||||
return SCons.Variables.PathVariable.PathIsDir(*args, **kw) | |||||
def PathIsDirCreate(self, *args, **kw): | |||||
self.warn() | |||||
return SCons.Variables.PathVariable.PathIsDirCreate(*args, **kw) | |||||
def PathIsFile(self, *args, **kw): | |||||
self.warn() | |||||
return SCons.Variables.PathVariable.PathIsFile(*args, **kw) | |||||
def PathExists(self, *args, **kw): | |||||
self.warn() | |||||
return SCons.Variables.PathVariable.PathExists(*args, **kw) | |||||
PathOption = _PathOptionClass() | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,67 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Options/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Place-holder for the old SCons.Options module hierarchy | |||||
This is for backwards compatibility. The new equivalent is the Variables/ | |||||
class hierarchy. These will have deprecation warnings added (some day), | |||||
and will then be removed entirely (some day). | |||||
""" | |||||
import SCons.Variables | |||||
import SCons.Warnings | |||||
from .BoolOption import BoolOption # okay | |||||
from .EnumOption import EnumOption # okay | |||||
from .ListOption import ListOption # naja | |||||
from .PackageOption import PackageOption # naja | |||||
from .PathOption import PathOption # okay | |||||
warned = False | |||||
class Options(SCons.Variables.Variables): | |||||
def __init__(self, *args, **kw): | |||||
global warned | |||||
if not warned: | |||||
msg = "The Options class is deprecated; use the Variables class instead." | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) | |||||
warned = True | |||||
SCons.Variables.Variables.__init__(self, *args, **kw) | |||||
def AddOptions(self, *args, **kw): | |||||
return SCons.Variables.Variables.AddVariables(self, *args, **kw) | |||||
def UnknownOptions(self, *args, **kw): | |||||
return SCons.Variables.Variables.UnknownVariables(self, *args, **kw) | |||||
def FormatOptionHelpText(self, *args, **kw): | |||||
return SCons.Variables.Variables.FormatVariableHelpText(self, *args, | |||||
**kw) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,227 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/PathList.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """SCons.PathList | |||||
A module for handling lists of directory paths (the sort of things | |||||
that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and | |||||
efficiency as we can, while still keeping the evaluation delayed so that we | |||||
Do the Right Thing (almost) regardless of how the variable is specified. | |||||
""" | |||||
import os | |||||
import SCons.Memoize | |||||
import SCons.Node | |||||
import SCons.Util | |||||
# | |||||
# Variables to specify the different types of entries in a PathList object: | |||||
# | |||||
TYPE_STRING_NO_SUBST = 0 # string with no '$' | |||||
TYPE_STRING_SUBST = 1 # string containing '$' | |||||
TYPE_OBJECT = 2 # other object | |||||
def node_conv(obj): | |||||
""" | |||||
This is the "string conversion" routine that we have our substitutions | |||||
use to return Nodes, not strings. This relies on the fact that an | |||||
EntryProxy object has a get() method that returns the underlying | |||||
Node that it wraps, which is a bit of architectural dependence | |||||
that we might need to break or modify in the future in response to | |||||
additional requirements. | |||||
""" | |||||
try: | |||||
get = obj.get | |||||
except AttributeError: | |||||
if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): | |||||
result = obj | |||||
else: | |||||
result = str(obj) | |||||
else: | |||||
result = get() | |||||
return result | |||||
class _PathList(object): | |||||
""" | |||||
An actual PathList object. | |||||
""" | |||||
def __init__(self, pathlist): | |||||
""" | |||||
Initializes a PathList object, canonicalizing the input and | |||||
pre-processing it for quicker substitution later. | |||||
The stored representation of the PathList is a list of tuples | |||||
containing (type, value), where the "type" is one of the TYPE_* | |||||
variables defined above. We distinguish between: | |||||
strings that contain no '$' and therefore need no | |||||
delayed-evaluation string substitution (we expect that there | |||||
will be many of these and that we therefore get a pretty | |||||
big win from avoiding string substitution) | |||||
strings that contain '$' and therefore need substitution | |||||
(the hard case is things like '${TARGET.dir}/include', | |||||
which require re-evaluation for every target + source) | |||||
other objects (which may be something like an EntryProxy | |||||
that needs a method called to return a Node) | |||||
Pre-identifying the type of each element in the PathList up-front | |||||
and storing the type in the list of tuples is intended to reduce | |||||
the amount of calculation when we actually do the substitution | |||||
over and over for each target. | |||||
""" | |||||
if SCons.Util.is_String(pathlist): | |||||
pathlist = pathlist.split(os.pathsep) | |||||
elif not SCons.Util.is_Sequence(pathlist): | |||||
pathlist = [pathlist] | |||||
pl = [] | |||||
for p in pathlist: | |||||
try: | |||||
found = '$' in p | |||||
except (AttributeError, TypeError): | |||||
type = TYPE_OBJECT | |||||
else: | |||||
if not found: | |||||
type = TYPE_STRING_NO_SUBST | |||||
else: | |||||
type = TYPE_STRING_SUBST | |||||
pl.append((type, p)) | |||||
self.pathlist = tuple(pl) | |||||
def __len__(self): return len(self.pathlist) | |||||
def __getitem__(self, i): return self.pathlist[i] | |||||
def subst_path(self, env, target, source): | |||||
""" | |||||
Performs construction variable substitution on a pre-digested | |||||
PathList for a specific target and source. | |||||
""" | |||||
result = [] | |||||
for type, value in self.pathlist: | |||||
if type == TYPE_STRING_SUBST: | |||||
value = env.subst(value, target=target, source=source, | |||||
conv=node_conv) | |||||
if SCons.Util.is_Sequence(value): | |||||
result.extend(SCons.Util.flatten(value)) | |||||
elif value: | |||||
result.append(value) | |||||
elif type == TYPE_OBJECT: | |||||
value = node_conv(value) | |||||
if value: | |||||
result.append(value) | |||||
elif value: | |||||
result.append(value) | |||||
return tuple(result) | |||||
class PathListCache(object): | |||||
""" | |||||
A class to handle caching of PathList lookups. | |||||
This class gets instantiated once and then deleted from the namespace, | |||||
so it's used as a Singleton (although we don't enforce that in the | |||||
usual Pythonic ways). We could have just made the cache a dictionary | |||||
in the module namespace, but putting it in this class allows us to | |||||
use the same Memoizer pattern that we use elsewhere to count cache | |||||
hits and misses, which is very valuable. | |||||
Lookup keys in the cache are computed by the _PathList_key() method. | |||||
Cache lookup should be quick, so we don't spend cycles canonicalizing | |||||
all forms of the same lookup key. For example, 'x:y' and ['x', | |||||
'y'] logically represent the same list, but we don't bother to | |||||
split string representations and treat those two equivalently. | |||||
(Note, however, that we do, treat lists and tuples the same.) | |||||
The main type of duplication we're trying to catch will come from | |||||
looking up the same path list from two different clones of the | |||||
same construction environment. That is, given | |||||
env2 = env1.Clone() | |||||
both env1 and env2 will have the same CPPPATH value, and we can | |||||
cheaply avoid re-parsing both values of CPPPATH by using the | |||||
common value from this cache. | |||||
""" | |||||
def __init__(self): | |||||
self._memo = {} | |||||
def _PathList_key(self, pathlist): | |||||
""" | |||||
Returns the key for memoization of PathLists. | |||||
Note that we want this to be pretty quick, so we don't completely | |||||
canonicalize all forms of the same list. For example, | |||||
'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically | |||||
represent the same list if you're executing from $ROOT, but | |||||
we're not going to bother splitting strings into path elements, | |||||
or massaging strings into Nodes, to identify that equivalence. | |||||
We just want to eliminate obvious redundancy from the normal | |||||
case of re-using exactly the same cloned value for a path. | |||||
""" | |||||
if SCons.Util.is_Sequence(pathlist): | |||||
pathlist = tuple(SCons.Util.flatten(pathlist)) | |||||
return pathlist | |||||
@SCons.Memoize.CountDictCall(_PathList_key) | |||||
def PathList(self, pathlist): | |||||
""" | |||||
Returns the cached _PathList object for the specified pathlist, | |||||
creating and caching a new object as necessary. | |||||
""" | |||||
pathlist = self._PathList_key(pathlist) | |||||
try: | |||||
memo_dict = self._memo['PathList'] | |||||
except KeyError: | |||||
memo_dict = {} | |||||
self._memo['PathList'] = memo_dict | |||||
else: | |||||
try: | |||||
return memo_dict[pathlist] | |||||
except KeyError: | |||||
pass | |||||
result = _PathList(pathlist) | |||||
memo_dict[pathlist] = result | |||||
return result | |||||
PathList = PathListCache().PathList | |||||
del PathListCache | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,267 @@ | |||||
"""SCons.Platform | |||||
SCons platform selection. | |||||
This looks for modules that define a callable object that can modify a | |||||
construction environment as appropriate for a given platform. | |||||
Note that we take a more simplistic view of "platform" than Python does. | |||||
We're looking for a single string that determines a set of | |||||
tool-independent variables with which to initialize a construction | |||||
environment. Consequently, we'll examine both sys.platform and os.name | |||||
(and anything else that might come in to play) in order to return some | |||||
specification which is unique enough for our purposes. | |||||
Note that because this subsystem just *selects* a callable that can | |||||
modify a construction environment, it's possible for people to define | |||||
their own "platform specification" in an arbitrary callable function. | |||||
No one needs to use or tie in to this subsystem in order to roll | |||||
their own platform definition. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
from __future__ import print_function | |||||
__revision__ = "src/engine/SCons/Platform/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.compat | |||||
import imp | |||||
import os | |||||
import sys | |||||
import tempfile | |||||
import SCons.Errors | |||||
import SCons.Subst | |||||
import SCons.Tool | |||||
def platform_default(): | |||||
"""Return the platform string for our execution environment. | |||||
The returned value should map to one of the SCons/Platform/*.py | |||||
files. Since we're architecture independent, though, we don't | |||||
care about the machine architecture. | |||||
""" | |||||
osname = os.name | |||||
if osname == 'java': | |||||
osname = os._osType | |||||
if osname == 'posix': | |||||
if sys.platform == 'cygwin': | |||||
return 'cygwin' | |||||
elif sys.platform.find('irix') != -1: | |||||
return 'irix' | |||||
elif sys.platform.find('sunos') != -1: | |||||
return 'sunos' | |||||
elif sys.platform.find('hp-ux') != -1: | |||||
return 'hpux' | |||||
elif sys.platform.find('aix') != -1: | |||||
return 'aix' | |||||
elif sys.platform.find('darwin') != -1: | |||||
return 'darwin' | |||||
else: | |||||
return 'posix' | |||||
elif os.name == 'os2': | |||||
return 'os2' | |||||
else: | |||||
return sys.platform | |||||
def platform_module(name = platform_default()): | |||||
"""Return the imported module for the platform. | |||||
This looks for a module name that matches the specified argument. | |||||
If the name is unspecified, we fetch the appropriate default for | |||||
our execution environment. | |||||
""" | |||||
full_name = 'SCons.Platform.' + name | |||||
if full_name not in sys.modules: | |||||
if os.name == 'java': | |||||
eval(full_name) | |||||
else: | |||||
try: | |||||
file, path, desc = imp.find_module(name, | |||||
sys.modules['SCons.Platform'].__path__) | |||||
try: | |||||
mod = imp.load_module(full_name, file, path, desc) | |||||
finally: | |||||
if file: | |||||
file.close() | |||||
except ImportError: | |||||
try: | |||||
import zipimport | |||||
importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] ) | |||||
mod = importer.load_module(full_name) | |||||
except ImportError: | |||||
raise SCons.Errors.UserError("No platform named '%s'" % name) | |||||
setattr(SCons.Platform, name, mod) | |||||
return sys.modules[full_name] | |||||
def DefaultToolList(platform, env): | |||||
"""Select a default tool list for the specified platform. | |||||
""" | |||||
return SCons.Tool.tool_list(platform, env) | |||||
class PlatformSpec(object): | |||||
def __init__(self, name, generate): | |||||
self.name = name | |||||
self.generate = generate | |||||
def __call__(self, *args, **kw): | |||||
return self.generate(*args, **kw) | |||||
def __str__(self): | |||||
return self.name | |||||
class TempFileMunge(object): | |||||
"""A callable class. You can set an Environment variable to this, | |||||
then call it with a string argument, then it will perform temporary | |||||
file substitution on it. This is used to circumvent the long command | |||||
line limitation. | |||||
Example usage: | |||||
env["TEMPFILE"] = TempFileMunge | |||||
env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES','$LINKCOMSTR')}" | |||||
By default, the name of the temporary file used begins with a | |||||
prefix of '@'. This may be configred for other tool chains by | |||||
setting '$TEMPFILEPREFIX'. | |||||
env["TEMPFILEPREFIX"] = '-@' # diab compiler | |||||
env["TEMPFILEPREFIX"] = '-via' # arm tool chain | |||||
""" | |||||
def __init__(self, cmd, cmdstr = None): | |||||
self.cmd = cmd | |||||
self.cmdstr = cmdstr | |||||
def __call__(self, target, source, env, for_signature): | |||||
if for_signature: | |||||
# If we're being called for signature calculation, it's | |||||
# because we're being called by the string expansion in | |||||
# Subst.py, which has the logic to strip any $( $) that | |||||
# may be in the command line we squirreled away. So we | |||||
# just return the raw command line and let the upper | |||||
# string substitution layers do their thing. | |||||
return self.cmd | |||||
# Now we're actually being called because someone is actually | |||||
# going to try to execute the command, so we have to do our | |||||
# own expansion. | |||||
cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0] | |||||
try: | |||||
maxline = int(env.subst('$MAXLINELENGTH')) | |||||
except ValueError: | |||||
maxline = 2048 | |||||
length = 0 | |||||
for c in cmd: | |||||
length += len(c) | |||||
length += len(cmd) - 1 | |||||
if length <= maxline: | |||||
return self.cmd | |||||
# Check if we already created the temporary file for this target | |||||
# It should have been previously done by Action.strfunction() call | |||||
node = target[0] if SCons.Util.is_List(target) else target | |||||
cmdlist = getattr(node.attributes, 'tempfile_cmdlist', None) \ | |||||
if node is not None else None | |||||
if cmdlist is not None : | |||||
return cmdlist | |||||
# We do a normpath because mktemp() has what appears to be | |||||
# a bug in Windows that will use a forward slash as a path | |||||
# delimiter. Windows's link mistakes that for a command line | |||||
# switch and barfs. | |||||
# | |||||
# We use the .lnk suffix for the benefit of the Phar Lap | |||||
# linkloc linker, which likes to append an .lnk suffix if | |||||
# none is given. | |||||
(fd, tmp) = tempfile.mkstemp('.lnk', text=True) | |||||
native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp)) | |||||
if env.get('SHELL',None) == 'sh': | |||||
# The sh shell will try to escape the backslashes in the | |||||
# path, so unescape them. | |||||
native_tmp = native_tmp.replace('\\', r'\\\\') | |||||
# In Cygwin, we want to use rm to delete the temporary | |||||
# file, because del does not exist in the sh shell. | |||||
rm = env.Detect('rm') or 'del' | |||||
else: | |||||
# Don't use 'rm' if the shell is not sh, because rm won't | |||||
# work with the Windows shells (cmd.exe or command.com) or | |||||
# Windows path names. | |||||
rm = 'del' | |||||
prefix = env.subst('$TEMPFILEPREFIX') | |||||
if not prefix: | |||||
prefix = '@' | |||||
args = list(map(SCons.Subst.quote_spaces, cmd[1:])) | |||||
os.write(fd, bytearray(" ".join(args) + "\n",'utf-8')) | |||||
os.close(fd) | |||||
# XXX Using the SCons.Action.print_actions value directly | |||||
# like this is bogus, but expedient. This class should | |||||
# really be rewritten as an Action that defines the | |||||
# __call__() and strfunction() methods and lets the | |||||
# normal action-execution logic handle whether or not to | |||||
# print/execute the action. The problem, though, is all | |||||
# of that is decided before we execute this method as | |||||
# part of expanding the $TEMPFILE construction variable. | |||||
# Consequently, refactoring this will have to wait until | |||||
# we get more flexible with allowing Actions to exist | |||||
# independently and get strung together arbitrarily like | |||||
# Ant tasks. In the meantime, it's going to be more | |||||
# user-friendly to not let obsession with architectural | |||||
# purity get in the way of just being helpful, so we'll | |||||
# reach into SCons.Action directly. | |||||
if SCons.Action.print_actions: | |||||
cmdstr = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, | |||||
source) if self.cmdstr is not None else '' | |||||
# Print our message only if XXXCOMSTR returns an empty string | |||||
if len(cmdstr) == 0 : | |||||
print("Using tempfile "+native_tmp+" for command line:\n"+ | |||||
str(cmd[0]) + " " + " ".join(args)) | |||||
# Store the temporary file command list into the target Node.attributes | |||||
# to avoid creating two temporary files one for print and one for execute. | |||||
cmdlist = [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] | |||||
if node is not None: | |||||
try : | |||||
setattr(node.attributes, 'tempfile_cmdlist', cmdlist) | |||||
except AttributeError: | |||||
pass | |||||
return cmdlist | |||||
def Platform(name = platform_default()): | |||||
"""Select a canned Platform specification. | |||||
""" | |||||
module = platform_module(name) | |||||
spec = PlatformSpec(name, module.generate) | |||||
return spec | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,85 @@ | |||||
"""engine.SCons.Platform.aix | |||||
Platform-specific initialization for IBM AIX systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/aix.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import subprocess | |||||
from . import posix | |||||
import SCons.Util | |||||
import SCons.Action | |||||
def get_xlc(env, xlc=None, packages=[]): | |||||
# Use the AIX package installer tool lslpp to figure out where a | |||||
# given xl* compiler is installed and what version it is. | |||||
xlcPath = None | |||||
xlcVersion = None | |||||
if xlc is None: | |||||
xlc = env.get('CC', 'xlc') | |||||
if SCons.Util.is_List(xlc): | |||||
xlc = xlc[0] | |||||
for package in packages: | |||||
# find the installed filename, which may be a symlink as well | |||||
pipe = SCons.Action._subproc(env, ['lslpp', '-fc', package], | |||||
stdin = 'devnull', | |||||
stderr = 'devnull', | |||||
stdout = subprocess.PIPE) | |||||
# output of lslpp is something like this: | |||||
# #Path:Fileset:File | |||||
# /usr/lib/objrepos:vac.C 6.0.0.0:/usr/vac/exe/xlCcpp | |||||
# /usr/lib/objrepos:vac.C 6.0.0.0:/usr/vac/bin/xlc_r -> /usr/vac/bin/xlc | |||||
for line in pipe.stdout: | |||||
if xlcPath: | |||||
continue # read everything to let lslpp terminate | |||||
fileset, filename = line.split(':')[1:3] | |||||
filename = filename.split()[0] | |||||
if ('/' in xlc and filename == xlc) \ | |||||
or ('/' not in xlc and filename.endswith('/' + xlc)): | |||||
xlcVersion = fileset.split()[1] | |||||
xlcPath, sep, xlc = filename.rpartition('/') | |||||
pass | |||||
pass | |||||
return (xlcPath, xlc, xlcVersion) | |||||
def generate(env): | |||||
posix.generate(env) | |||||
#Based on AIX 5.2: ARG_MAX=24576 - 3000 for environment expansion | |||||
env['MAXLINELENGTH'] = 21576 | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,55 @@ | |||||
"""SCons.Platform.cygwin | |||||
Platform-specific initialization for Cygwin systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/cygwin.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from . import posix | |||||
from SCons.Platform import TempFileMunge | |||||
def generate(env): | |||||
posix.generate(env) | |||||
env['PROGPREFIX'] = '' | |||||
env['PROGSUFFIX'] = '.exe' | |||||
env['SHLIBPREFIX'] = '' | |||||
env['SHLIBSUFFIX'] = '.dll' | |||||
env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX', '$IMPLIBPREFIX' ] | |||||
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX', '$IMPLIBSUFFIX' ] | |||||
env['TEMPFILE'] = TempFileMunge | |||||
env['TEMPFILEPREFIX'] = '@' | |||||
env['MAXLINELENGTH'] = 2048 | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,74 @@ | |||||
"""engine.SCons.Platform.darwin | |||||
Platform-specific initialization for Mac OS X systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/darwin.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from . import posix | |||||
import os | |||||
def generate(env): | |||||
posix.generate(env) | |||||
env['SHLIBSUFFIX'] = '.dylib' | |||||
# put macports paths at front to override Apple's versions, fink path is after | |||||
# For now let people who want Macports or Fink tools specify it! | |||||
# env['ENV']['PATH'] = '/opt/local/bin:/opt/local/sbin:' + env['ENV']['PATH'] + ':/sw/bin' | |||||
# Store extra system paths in env['ENV']['PATHOSX'] | |||||
filelist = ['/etc/paths',] | |||||
# make sure this works on Macs with Tiger or earlier | |||||
try: | |||||
dirlist = os.listdir('/etc/paths.d') | |||||
except: | |||||
dirlist = [] | |||||
for file in dirlist: | |||||
filelist.append('/etc/paths.d/'+file) | |||||
for file in filelist: | |||||
if os.path.isfile(file): | |||||
f = open(file, 'r') | |||||
lines = f.readlines() | |||||
for line in lines: | |||||
if line: | |||||
env.AppendENVPath('PATHOSX', line.strip('\n')) | |||||
f.close() | |||||
# Not sure why this wasn't the case all along? | |||||
if env['ENV'].get('PATHOSX', False) and os.environ.get('SCONS_USE_MAC_PATHS', False): | |||||
env.AppendENVPath('PATH',env['ENV']['PATHOSX']) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,46 @@ | |||||
"""engine.SCons.Platform.hpux | |||||
Platform-specific initialization for HP-UX systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/hpux.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from . import posix | |||||
def generate(env): | |||||
posix.generate(env) | |||||
#Based on HP-UX11i: ARG_MAX=2048000 - 3000 for environment expansion | |||||
env['MAXLINELENGTH'] = 2045000 | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,44 @@ | |||||
"""SCons.Platform.irix | |||||
Platform-specific initialization for SGI IRIX systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/irix.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from . import posix | |||||
def generate(env): | |||||
posix.generate(env) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,58 @@ | |||||
"""SCons.Platform.os2 | |||||
Platform-specific initialization for OS/2 systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/os2.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from . import win32 | |||||
def generate(env): | |||||
if 'ENV' not in env: | |||||
env['ENV'] = {} | |||||
env['OBJPREFIX'] = '' | |||||
env['OBJSUFFIX'] = '.obj' | |||||
env['SHOBJPREFIX'] = '$OBJPREFIX' | |||||
env['SHOBJSUFFIX'] = '$OBJSUFFIX' | |||||
env['PROGPREFIX'] = '' | |||||
env['PROGSUFFIX'] = '.exe' | |||||
env['LIBPREFIX'] = '' | |||||
env['LIBSUFFIX'] = '.lib' | |||||
env['SHLIBPREFIX'] = '' | |||||
env['SHLIBSUFFIX'] = '.dll' | |||||
env['LIBPREFIXES'] = '$LIBPREFIX' | |||||
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] | |||||
env['HOST_OS'] = 'os2' | |||||
env['HOST_ARCH'] = win32.get_architecture().arch | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,126 @@ | |||||
"""SCons.Platform.posix | |||||
Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/posix.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import errno | |||||
import os | |||||
import os.path | |||||
import subprocess | |||||
import sys | |||||
import select | |||||
import SCons.Util | |||||
from SCons.Platform import TempFileMunge | |||||
exitvalmap = { | |||||
2 : 127, | |||||
13 : 126, | |||||
} | |||||
def escape(arg): | |||||
"escape shell special characters" | |||||
slash = '\\' | |||||
special = '"$' | |||||
arg = arg.replace(slash, slash+slash) | |||||
for c in special: | |||||
arg = arg.replace(c, slash+c) | |||||
# print("ESCAPE RESULT: %s" % arg) | |||||
return '"' + arg + '"' | |||||
def exec_subprocess(l, env): | |||||
proc = subprocess.Popen(l, env = env, close_fds = True) | |||||
return proc.wait() | |||||
def subprocess_spawn(sh, escape, cmd, args, env): | |||||
return exec_subprocess([sh, '-c', ' '.join(args)], env) | |||||
def exec_popen3(l, env, stdout, stderr): | |||||
proc = subprocess.Popen(l, env = env, close_fds = True, | |||||
stdout = stdout, | |||||
stderr = stderr) | |||||
return proc.wait() | |||||
def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr): | |||||
# spawn using Popen3 combined with the env command | |||||
# the command name and the command's stdout is written to stdout | |||||
# the command's stderr is written to stderr | |||||
return exec_popen3([sh, '-c', ' '.join(args)], | |||||
env, stdout, stderr) | |||||
def generate(env): | |||||
# Bearing in mind we have python 2.4 as a baseline, we can just do this: | |||||
spawn = subprocess_spawn | |||||
pspawn = piped_env_spawn | |||||
# Note that this means that 'escape' is no longer used | |||||
if 'ENV' not in env: | |||||
env['ENV'] = {} | |||||
env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin' | |||||
env['OBJPREFIX'] = '' | |||||
env['OBJSUFFIX'] = '.o' | |||||
env['SHOBJPREFIX'] = '$OBJPREFIX' | |||||
env['SHOBJSUFFIX'] = '$OBJSUFFIX' | |||||
env['PROGPREFIX'] = '' | |||||
env['PROGSUFFIX'] = '' | |||||
env['LIBPREFIX'] = 'lib' | |||||
env['LIBSUFFIX'] = '.a' | |||||
env['SHLIBPREFIX'] = '$LIBPREFIX' | |||||
env['SHLIBSUFFIX'] = '.so' | |||||
env['LIBPREFIXES'] = [ '$LIBPREFIX' ] | |||||
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] | |||||
env['PSPAWN'] = pspawn | |||||
env['SPAWN'] = spawn | |||||
env['SHELL'] = 'sh' | |||||
env['ESCAPE'] = escape | |||||
env['TEMPFILE'] = TempFileMunge | |||||
env['TEMPFILEPREFIX'] = '@' | |||||
#Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion | |||||
#Note: specific platforms might rise or lower this value | |||||
env['MAXLINELENGTH'] = 128072 | |||||
# This platform supports RPATH specifications. | |||||
env['__RPATH'] = '$_RPATH' | |||||
# GDC is GCC family, but DMD and LDC have different options. | |||||
# Must be able to have GCC and DMD work in the same build, so: | |||||
env['__DRPATH'] = '$_DRPATH' | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,50 @@ | |||||
"""engine.SCons.Platform.sunos | |||||
Platform-specific initialization for Sun systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/sunos.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from . import posix | |||||
def generate(env): | |||||
posix.generate(env) | |||||
# Based on sunSparc 8:32bit | |||||
# ARG_MAX=1048320 - 3000 for environment expansion | |||||
env['MAXLINELENGTH'] = 1045320 | |||||
env['PKGINFO'] = 'pkginfo' | |||||
env['PKGCHK'] = '/usr/sbin/pkgchk' | |||||
env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,470 @@ | |||||
"""SCons.Platform.win32 | |||||
Platform-specific initialization for Win32 systems. | |||||
There normally shouldn't be any need to import this module directly. It | |||||
will usually be imported through the generic SCons.Platform.Platform() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Platform/win32.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import os.path | |||||
import sys | |||||
import tempfile | |||||
from SCons.Platform.posix import exitvalmap | |||||
from SCons.Platform import TempFileMunge | |||||
import SCons.Util | |||||
try: | |||||
import msvcrt | |||||
import win32api | |||||
import win32con | |||||
msvcrt.get_osfhandle | |||||
win32api.SetHandleInformation | |||||
win32con.HANDLE_FLAG_INHERIT | |||||
except ImportError: | |||||
parallel_msg = \ | |||||
"you do not seem to have the pywin32 extensions installed;\n" + \ | |||||
"\tparallel (-j) builds may not work reliably with open Python files." | |||||
except AttributeError: | |||||
parallel_msg = \ | |||||
"your pywin32 extensions do not support file handle operations;\n" + \ | |||||
"\tparallel (-j) builds may not work reliably with open Python files." | |||||
else: | |||||
parallel_msg = None | |||||
_builtin_open = open | |||||
def _scons_open(*args, **kw): | |||||
fp = _builtin_open(*args, **kw) | |||||
win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), | |||||
win32con.HANDLE_FLAG_INHERIT, | |||||
0) | |||||
return fp | |||||
open = _scons_open | |||||
if sys.version_info.major == 2: | |||||
_builtin_file = file | |||||
class _scons_file(_builtin_file): | |||||
def __init__(self, *args, **kw): | |||||
_builtin_file.__init__(self, *args, **kw) | |||||
win32api.SetHandleInformation(msvcrt.get_osfhandle(self.fileno()), | |||||
win32con.HANDLE_FLAG_INHERIT, 0) | |||||
file = _scons_file | |||||
else: | |||||
import io | |||||
for io_class in ['BufferedReader', 'BufferedWriter', 'BufferedRWPair', | |||||
'BufferedRandom', 'TextIOWrapper']: | |||||
_builtin_file = getattr(io, io_class) | |||||
class _scons_file(_builtin_file): | |||||
def __init__(self, *args, **kw): | |||||
_builtin_file.__init__(self, *args, **kw) | |||||
win32api.SetHandleInformation(msvcrt.get_osfhandle(self.fileno()), | |||||
win32con.HANDLE_FLAG_INHERIT, 0) | |||||
setattr(io, io_class, _scons_file) | |||||
if False: | |||||
# Now swap out shutil.filecopy and filecopy2 for win32 api native CopyFile | |||||
try: | |||||
from ctypes import windll | |||||
import shutil | |||||
CopyFile = windll.kernel32.CopyFileA | |||||
SetFileTime = windll.kernel32.SetFileTime | |||||
_shutil_copy = shutil.copy | |||||
_shutil_copy2 = shutil.copy2 | |||||
shutil.copy2 = CopyFile | |||||
def win_api_copyfile(src,dst): | |||||
CopyFile(src,dst) | |||||
os.utime(dst) | |||||
shutil.copy = win_api_copyfile | |||||
except AttributeError: | |||||
parallel_msg = \ | |||||
"Couldn't override shutil.copy or shutil.copy2 falling back to shutil defaults" | |||||
try: | |||||
import threading | |||||
spawn_lock = threading.Lock() | |||||
# This locked version of spawnve works around a Windows | |||||
# MSVCRT bug, because its spawnve is not thread-safe. | |||||
# Without this, python can randomly crash while using -jN. | |||||
# See the python bug at http://bugs.python.org/issue6476 | |||||
# and SCons issue at | |||||
# http://scons.tigris.org/issues/show_bug.cgi?id=2449 | |||||
def spawnve(mode, file, args, env): | |||||
spawn_lock.acquire() | |||||
try: | |||||
if mode == os.P_WAIT: | |||||
ret = os.spawnve(os.P_NOWAIT, file, args, env) | |||||
else: | |||||
ret = os.spawnve(mode, file, args, env) | |||||
finally: | |||||
spawn_lock.release() | |||||
if mode == os.P_WAIT: | |||||
pid, status = os.waitpid(ret, 0) | |||||
ret = status >> 8 | |||||
return ret | |||||
except ImportError: | |||||
# Use the unsafe method of spawnve. | |||||
# Please, don't try to optimize this try-except block | |||||
# away by assuming that the threading module is always present. | |||||
# In the test test/option-j.py we intentionally call SCons with | |||||
# a fake threading.py that raises an import exception right away, | |||||
# simulating a non-existent package. | |||||
def spawnve(mode, file, args, env): | |||||
return os.spawnve(mode, file, args, env) | |||||
# The upshot of all this is that, if you are using Python 1.5.2, | |||||
# you had better have cmd or command.com in your PATH when you run | |||||
# scons. | |||||
def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): | |||||
# There is no direct way to do that in python. What we do | |||||
# here should work for most cases: | |||||
# In case stdout (stderr) is not redirected to a file, | |||||
# we redirect it into a temporary file tmpFileStdout | |||||
# (tmpFileStderr) and copy the contents of this file | |||||
# to stdout (stderr) given in the argument | |||||
if not sh: | |||||
sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") | |||||
return 127 | |||||
else: | |||||
# one temporary file for stdout and stderr | |||||
tmpFileStdout = os.path.normpath(tempfile.mktemp()) | |||||
tmpFileStderr = os.path.normpath(tempfile.mktemp()) | |||||
# check if output is redirected | |||||
stdoutRedirected = 0 | |||||
stderrRedirected = 0 | |||||
for arg in args: | |||||
# are there more possibilities to redirect stdout ? | |||||
if arg.find( ">", 0, 1 ) != -1 or arg.find( "1>", 0, 2 ) != -1: | |||||
stdoutRedirected = 1 | |||||
# are there more possibilities to redirect stderr ? | |||||
if arg.find( "2>", 0, 2 ) != -1: | |||||
stderrRedirected = 1 | |||||
# redirect output of non-redirected streams to our tempfiles | |||||
if stdoutRedirected == 0: | |||||
args.append(">" + str(tmpFileStdout)) | |||||
if stderrRedirected == 0: | |||||
args.append("2>" + str(tmpFileStderr)) | |||||
# actually do the spawn | |||||
try: | |||||
args = [sh, '/C', escape(' '.join(args)) ] | |||||
ret = spawnve(os.P_WAIT, sh, args, env) | |||||
except OSError as e: | |||||
# catch any error | |||||
try: | |||||
ret = exitvalmap[e[0]] | |||||
except KeyError: | |||||
sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) | |||||
if stderr is not None: | |||||
stderr.write("scons: %s: %s\n" % (cmd, e[1])) | |||||
# copy child output from tempfiles to our streams | |||||
# and do clean up stuff | |||||
if stdout is not None and stdoutRedirected == 0: | |||||
try: | |||||
stdout.write(open( tmpFileStdout, "r" ).read()) | |||||
os.remove( tmpFileStdout ) | |||||
except (IOError, OSError): | |||||
pass | |||||
if stderr is not None and stderrRedirected == 0: | |||||
try: | |||||
stderr.write(open( tmpFileStderr, "r" ).read()) | |||||
os.remove( tmpFileStderr ) | |||||
except (IOError, OSError): | |||||
pass | |||||
return ret | |||||
def exec_spawn(l, env): | |||||
try: | |||||
result = spawnve(os.P_WAIT, l[0], l, env) | |||||
except (OSError, EnvironmentError) as e: | |||||
try: | |||||
result = exitvalmap[e.errno] | |||||
sys.stderr.write("scons: %s: %s\n" % (l[0], e.strerror)) | |||||
except KeyError: | |||||
result = 127 | |||||
if len(l) > 2: | |||||
if len(l[2]) < 1000: | |||||
command = ' '.join(l[0:3]) | |||||
else: | |||||
command = l[0] | |||||
else: | |||||
command = l[0] | |||||
sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e.errno, command, e.strerror)) | |||||
return result | |||||
def spawn(sh, escape, cmd, args, env): | |||||
if not sh: | |||||
sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") | |||||
return 127 | |||||
return exec_spawn([sh, '/C', escape(' '.join(args))], env) | |||||
# Windows does not allow special characters in file names anyway, so no | |||||
# need for a complex escape function, we will just quote the arg, except | |||||
# that "cmd /c" requires that if an argument ends with a backslash it | |||||
# needs to be escaped so as not to interfere with closing double quote | |||||
# that we add. | |||||
def escape(x): | |||||
if x[-1] == '\\': | |||||
x = x + '\\' | |||||
return '"' + x + '"' | |||||
# Get the windows system directory name | |||||
_system_root = None | |||||
def get_system_root(): | |||||
global _system_root | |||||
if _system_root is not None: | |||||
return _system_root | |||||
# A resonable default if we can't read the registry | |||||
val = os.environ.get('SystemRoot', "C:\\WINDOWS") | |||||
if SCons.Util.can_read_reg: | |||||
try: | |||||
# Look for Windows NT system root | |||||
k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, | |||||
'Software\\Microsoft\\Windows NT\\CurrentVersion') | |||||
val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') | |||||
except SCons.Util.RegError: | |||||
try: | |||||
# Okay, try the Windows 9x system root | |||||
k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, | |||||
'Software\\Microsoft\\Windows\\CurrentVersion') | |||||
val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') | |||||
except KeyboardInterrupt: | |||||
raise | |||||
except: | |||||
pass | |||||
# Ensure system root is a string and not unicode | |||||
# (This only matters for py27 were unicode in env passed to POpen fails) | |||||
val = str(val) | |||||
_system_root = val | |||||
return val | |||||
def get_program_files_dir(): | |||||
""" | |||||
Get the location of the program files directory | |||||
Returns | |||||
------- | |||||
""" | |||||
# Now see if we can look in the registry... | |||||
val = '' | |||||
if SCons.Util.can_read_reg: | |||||
try: | |||||
# Look for Windows Program Files directory | |||||
k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, | |||||
'Software\\Microsoft\\Windows\\CurrentVersion') | |||||
val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir') | |||||
except SCons.Util.RegError: | |||||
val = '' | |||||
pass | |||||
if val == '': | |||||
# A reasonable default if we can't read the registry | |||||
# (Actually, it's pretty reasonable even if we can :-) | |||||
val = os.path.join(os.path.dirname(get_system_root()),"Program Files") | |||||
return val | |||||
class ArchDefinition(object): | |||||
""" | |||||
Determine which windows CPU were running on. | |||||
A class for defining architecture-specific settings and logic. | |||||
""" | |||||
def __init__(self, arch, synonyms=[]): | |||||
self.arch = arch | |||||
self.synonyms = synonyms | |||||
SupportedArchitectureList = [ | |||||
ArchDefinition( | |||||
'x86', | |||||
['i386', 'i486', 'i586', 'i686'], | |||||
), | |||||
ArchDefinition( | |||||
'x86_64', | |||||
['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], | |||||
), | |||||
ArchDefinition( | |||||
'ia64', | |||||
['IA64'], | |||||
), | |||||
] | |||||
SupportedArchitectureMap = {} | |||||
for a in SupportedArchitectureList: | |||||
SupportedArchitectureMap[a.arch] = a | |||||
for s in a.synonyms: | |||||
SupportedArchitectureMap[s] = a | |||||
def get_architecture(arch=None): | |||||
"""Returns the definition for the specified architecture string. | |||||
If no string is specified, the system default is returned (as defined | |||||
by the PROCESSOR_ARCHITEW6432 or PROCESSOR_ARCHITECTURE environment | |||||
variables). | |||||
""" | |||||
if arch is None: | |||||
arch = os.environ.get('PROCESSOR_ARCHITEW6432') | |||||
if not arch: | |||||
arch = os.environ.get('PROCESSOR_ARCHITECTURE') | |||||
return SupportedArchitectureMap.get(arch, ArchDefinition('', [''])) | |||||
def generate(env): | |||||
# Attempt to find cmd.exe (for WinNT/2k/XP) or | |||||
# command.com for Win9x | |||||
cmd_interp = '' | |||||
# First see if we can look in the registry... | |||||
if SCons.Util.can_read_reg: | |||||
try: | |||||
# Look for Windows NT system root | |||||
k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, | |||||
'Software\\Microsoft\\Windows NT\\CurrentVersion') | |||||
val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') | |||||
cmd_interp = os.path.join(val, 'System32\\cmd.exe') | |||||
except SCons.Util.RegError: | |||||
try: | |||||
# Okay, try the Windows 9x system root | |||||
k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, | |||||
'Software\\Microsoft\\Windows\\CurrentVersion') | |||||
val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') | |||||
cmd_interp = os.path.join(val, 'command.com') | |||||
except KeyboardInterrupt: | |||||
raise | |||||
except: | |||||
pass | |||||
# For the special case of not having access to the registry, we | |||||
# use a temporary path and pathext to attempt to find the command | |||||
# interpreter. If we fail, we try to find the interpreter through | |||||
# the env's PATH. The problem with that is that it might not | |||||
# contain an ENV and a PATH. | |||||
if not cmd_interp: | |||||
systemroot = get_system_root() | |||||
tmp_path = systemroot + os.pathsep + \ | |||||
os.path.join(systemroot,'System32') | |||||
tmp_pathext = '.com;.exe;.bat;.cmd' | |||||
if 'PATHEXT' in os.environ: | |||||
tmp_pathext = os.environ['PATHEXT'] | |||||
cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) | |||||
if not cmd_interp: | |||||
cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) | |||||
if not cmd_interp: | |||||
cmd_interp = env.Detect('cmd') | |||||
if not cmd_interp: | |||||
cmd_interp = env.Detect('command') | |||||
if 'ENV' not in env: | |||||
env['ENV'] = {} | |||||
# Import things from the external environment to the construction | |||||
# environment's ENV. This is a potential slippery slope, because we | |||||
# *don't* want to make builds dependent on the user's environment by | |||||
# default. We're doing this for SystemRoot, though, because it's | |||||
# needed for anything that uses sockets, and seldom changes, and | |||||
# for SystemDrive because it's related. | |||||
# | |||||
# Weigh the impact carefully before adding other variables to this list. | |||||
import_env = ['SystemDrive', 'SystemRoot', 'TEMP', 'TMP' ] | |||||
for var in import_env: | |||||
v = os.environ.get(var) | |||||
if v: | |||||
env['ENV'][var] = v | |||||
if 'COMSPEC' not in env['ENV']: | |||||
v = os.environ.get("COMSPEC") | |||||
if v: | |||||
env['ENV']['COMSPEC'] = v | |||||
env.AppendENVPath('PATH', get_system_root() + '\System32') | |||||
env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD' | |||||
env['OBJPREFIX'] = '' | |||||
env['OBJSUFFIX'] = '.obj' | |||||
env['SHOBJPREFIX'] = '$OBJPREFIX' | |||||
env['SHOBJSUFFIX'] = '$OBJSUFFIX' | |||||
env['PROGPREFIX'] = '' | |||||
env['PROGSUFFIX'] = '.exe' | |||||
env['LIBPREFIX'] = '' | |||||
env['LIBSUFFIX'] = '.lib' | |||||
env['SHLIBPREFIX'] = '' | |||||
env['SHLIBSUFFIX'] = '.dll' | |||||
env['LIBPREFIXES'] = [ '$LIBPREFIX' ] | |||||
env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] | |||||
env['PSPAWN'] = piped_spawn | |||||
env['SPAWN'] = spawn | |||||
env['SHELL'] = cmd_interp | |||||
env['TEMPFILE'] = TempFileMunge | |||||
env['TEMPFILEPREFIX'] = '@' | |||||
env['MAXLINELENGTH'] = 2048 | |||||
env['ESCAPE'] = escape | |||||
env['HOST_OS'] = 'win32' | |||||
env['HOST_ARCH'] = get_architecture().arch | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,426 @@ | |||||
"""SCons.SConsign | |||||
Writing and reading information to the .sconsign file or files. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
from __future__ import print_function | |||||
__revision__ = "src/engine/SCons/SConsign.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.compat | |||||
import os | |||||
import pickle | |||||
import SCons.dblite | |||||
import SCons.Warnings | |||||
from SCons.compat import PICKLE_PROTOCOL | |||||
def corrupt_dblite_warning(filename): | |||||
SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, | |||||
"Ignoring corrupt .sconsign file: %s"%filename) | |||||
SCons.dblite.ignore_corrupt_dbfiles = 1 | |||||
SCons.dblite.corruption_warning = corrupt_dblite_warning | |||||
# XXX Get rid of the global array so this becomes re-entrant. | |||||
sig_files = [] | |||||
# Info for the database SConsign implementation (now the default): | |||||
# "DataBase" is a dictionary that maps top-level SConstruct directories | |||||
# to open database handles. | |||||
# "DB_Module" is the Python database module to create the handles. | |||||
# "DB_Name" is the base name of the database file (minus any | |||||
# extension the underlying DB module will add). | |||||
DataBase = {} | |||||
DB_Module = SCons.dblite | |||||
DB_Name = ".sconsign" | |||||
DB_sync_list = [] | |||||
def Get_DataBase(dir): | |||||
global DataBase, DB_Module, DB_Name | |||||
top = dir.fs.Top | |||||
if not os.path.isabs(DB_Name) and top.repositories: | |||||
mode = "c" | |||||
for d in [top] + top.repositories: | |||||
if dir.is_under(d): | |||||
try: | |||||
return DataBase[d], mode | |||||
except KeyError: | |||||
path = d.entry_abspath(DB_Name) | |||||
try: db = DataBase[d] = DB_Module.open(path, mode) | |||||
except (IOError, OSError): pass | |||||
else: | |||||
if mode != "r": | |||||
DB_sync_list.append(db) | |||||
return db, mode | |||||
mode = "r" | |||||
try: | |||||
return DataBase[top], "c" | |||||
except KeyError: | |||||
db = DataBase[top] = DB_Module.open(DB_Name, "c") | |||||
DB_sync_list.append(db) | |||||
return db, "c" | |||||
except TypeError: | |||||
print("DataBase =", DataBase) | |||||
raise | |||||
def Reset(): | |||||
"""Reset global state. Used by unit tests that end up using | |||||
SConsign multiple times to get a clean slate for each test.""" | |||||
global sig_files, DB_sync_list | |||||
sig_files = [] | |||||
DB_sync_list = [] | |||||
normcase = os.path.normcase | |||||
def write(): | |||||
global sig_files | |||||
for sig_file in sig_files: | |||||
sig_file.write(sync=0) | |||||
for db in DB_sync_list: | |||||
try: | |||||
syncmethod = db.sync | |||||
except AttributeError: | |||||
pass # Not all dbm modules have sync() methods. | |||||
else: | |||||
syncmethod() | |||||
try: | |||||
closemethod = db.close | |||||
except AttributeError: | |||||
pass # Not all dbm modules have close() methods. | |||||
else: | |||||
closemethod() | |||||
class SConsignEntry(object): | |||||
""" | |||||
Wrapper class for the generic entry in a .sconsign file. | |||||
The Node subclass populates it with attributes as it pleases. | |||||
XXX As coded below, we do expect a '.binfo' attribute to be added, | |||||
but we'll probably generalize this in the next refactorings. | |||||
""" | |||||
__slots__ = ("binfo", "ninfo", "__weakref__") | |||||
current_version_id = 2 | |||||
def __init__(self): | |||||
# Create an object attribute from the class attribute so it ends up | |||||
# in the pickled data in the .sconsign file. | |||||
#_version_id = self.current_version_id | |||||
pass | |||||
def convert_to_sconsign(self): | |||||
self.binfo.convert_to_sconsign() | |||||
def convert_from_sconsign(self, dir, name): | |||||
self.binfo.convert_from_sconsign(dir, name) | |||||
def __getstate__(self): | |||||
state = getattr(self, '__dict__', {}).copy() | |||||
for obj in type(self).mro(): | |||||
for name in getattr(obj,'__slots__',()): | |||||
if hasattr(self, name): | |||||
state[name] = getattr(self, name) | |||||
state['_version_id'] = self.current_version_id | |||||
try: | |||||
del state['__weakref__'] | |||||
except KeyError: | |||||
pass | |||||
return state | |||||
def __setstate__(self, state): | |||||
for key, value in state.items(): | |||||
if key not in ('_version_id','__weakref__'): | |||||
setattr(self, key, value) | |||||
class Base(object): | |||||
""" | |||||
This is the controlling class for the signatures for the collection of | |||||
entries associated with a specific directory. The actual directory | |||||
association will be maintained by a subclass that is specific to | |||||
the underlying storage method. This class provides a common set of | |||||
methods for fetching and storing the individual bits of information | |||||
that make up signature entry. | |||||
""" | |||||
def __init__(self): | |||||
self.entries = {} | |||||
self.dirty = False | |||||
self.to_be_merged = {} | |||||
def get_entry(self, filename): | |||||
""" | |||||
Fetch the specified entry attribute. | |||||
""" | |||||
return self.entries[filename] | |||||
def set_entry(self, filename, obj): | |||||
""" | |||||
Set the entry. | |||||
""" | |||||
self.entries[filename] = obj | |||||
self.dirty = True | |||||
def do_not_set_entry(self, filename, obj): | |||||
pass | |||||
def store_info(self, filename, node): | |||||
entry = node.get_stored_info() | |||||
entry.binfo.merge(node.get_binfo()) | |||||
self.to_be_merged[filename] = node | |||||
self.dirty = True | |||||
def do_not_store_info(self, filename, node): | |||||
pass | |||||
def merge(self): | |||||
for key, node in self.to_be_merged.items(): | |||||
entry = node.get_stored_info() | |||||
try: | |||||
ninfo = entry.ninfo | |||||
except AttributeError: | |||||
# This happens with SConf Nodes, because the configuration | |||||
# subsystem takes direct control over how the build decision | |||||
# is made and its information stored. | |||||
pass | |||||
else: | |||||
ninfo.merge(node.get_ninfo()) | |||||
self.entries[key] = entry | |||||
self.to_be_merged = {} | |||||
class DB(Base): | |||||
""" | |||||
A Base subclass that reads and writes signature information | |||||
from a global .sconsign.db* file--the actual file suffix is | |||||
determined by the database module. | |||||
""" | |||||
def __init__(self, dir): | |||||
Base.__init__(self) | |||||
self.dir = dir | |||||
db, mode = Get_DataBase(dir) | |||||
# Read using the path relative to the top of the Repository | |||||
# (self.dir.tpath) from which we're fetching the signature | |||||
# information. | |||||
path = normcase(dir.get_tpath()) | |||||
try: | |||||
rawentries = db[path] | |||||
except KeyError: | |||||
pass | |||||
else: | |||||
try: | |||||
self.entries = pickle.loads(rawentries) | |||||
if not isinstance(self.entries, dict): | |||||
self.entries = {} | |||||
raise TypeError | |||||
except KeyboardInterrupt: | |||||
raise | |||||
except Exception as e: | |||||
SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, | |||||
"Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.get_tpath(), e)) | |||||
for key, entry in self.entries.items(): | |||||
entry.convert_from_sconsign(dir, key) | |||||
if mode == "r": | |||||
# This directory is actually under a repository, which means | |||||
# likely they're reaching in directly for a dependency on | |||||
# a file there. Don't actually set any entry info, so we | |||||
# won't try to write to that .sconsign.dblite file. | |||||
self.set_entry = self.do_not_set_entry | |||||
self.store_info = self.do_not_store_info | |||||
global sig_files | |||||
sig_files.append(self) | |||||
def write(self, sync=1): | |||||
if not self.dirty: | |||||
return | |||||
self.merge() | |||||
db, mode = Get_DataBase(self.dir) | |||||
# Write using the path relative to the top of the SConstruct | |||||
# directory (self.dir.path), not relative to the top of | |||||
# the Repository; we only write to our own .sconsign file, | |||||
# not to .sconsign files in Repositories. | |||||
path = normcase(self.dir.get_internal_path()) | |||||
for key, entry in self.entries.items(): | |||||
entry.convert_to_sconsign() | |||||
db[path] = pickle.dumps(self.entries, PICKLE_PROTOCOL) | |||||
if sync: | |||||
try: | |||||
syncmethod = db.sync | |||||
except AttributeError: | |||||
# Not all anydbm modules have sync() methods. | |||||
pass | |||||
else: | |||||
syncmethod() | |||||
class Dir(Base): | |||||
def __init__(self, fp=None, dir=None): | |||||
""" | |||||
fp - file pointer to read entries from | |||||
""" | |||||
Base.__init__(self) | |||||
if not fp: | |||||
return | |||||
self.entries = pickle.load(fp) | |||||
if not isinstance(self.entries, dict): | |||||
self.entries = {} | |||||
raise TypeError | |||||
if dir: | |||||
for key, entry in self.entries.items(): | |||||
entry.convert_from_sconsign(dir, key) | |||||
class DirFile(Dir): | |||||
""" | |||||
Encapsulates reading and writing a per-directory .sconsign file. | |||||
""" | |||||
def __init__(self, dir): | |||||
""" | |||||
dir - the directory for the file | |||||
""" | |||||
self.dir = dir | |||||
self.sconsign = os.path.join(dir.get_internal_path(), '.sconsign') | |||||
try: | |||||
fp = open(self.sconsign, 'rb') | |||||
except IOError: | |||||
fp = None | |||||
try: | |||||
Dir.__init__(self, fp, dir) | |||||
except KeyboardInterrupt: | |||||
raise | |||||
except: | |||||
SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, | |||||
"Ignoring corrupt .sconsign file: %s"%self.sconsign) | |||||
global sig_files | |||||
sig_files.append(self) | |||||
def write(self, sync=1): | |||||
""" | |||||
Write the .sconsign file to disk. | |||||
Try to write to a temporary file first, and rename it if we | |||||
succeed. If we can't write to the temporary file, it's | |||||
probably because the directory isn't writable (and if so, | |||||
how did we build anything in this directory, anyway?), so | |||||
try to write directly to the .sconsign file as a backup. | |||||
If we can't rename, try to copy the temporary contents back | |||||
to the .sconsign file. Either way, always try to remove | |||||
the temporary file at the end. | |||||
""" | |||||
if not self.dirty: | |||||
return | |||||
self.merge() | |||||
temp = os.path.join(self.dir.get_internal_path(), '.scons%d' % os.getpid()) | |||||
try: | |||||
file = open(temp, 'wb') | |||||
fname = temp | |||||
except IOError: | |||||
try: | |||||
file = open(self.sconsign, 'wb') | |||||
fname = self.sconsign | |||||
except IOError: | |||||
return | |||||
for key, entry in self.entries.items(): | |||||
entry.convert_to_sconsign() | |||||
pickle.dump(self.entries, file, PICKLE_PROTOCOL) | |||||
file.close() | |||||
if fname != self.sconsign: | |||||
try: | |||||
mode = os.stat(self.sconsign)[0] | |||||
os.chmod(self.sconsign, 0o666) | |||||
os.unlink(self.sconsign) | |||||
except (IOError, OSError): | |||||
# Try to carry on in the face of either OSError | |||||
# (things like permission issues) or IOError (disk | |||||
# or network issues). If there's a really dangerous | |||||
# issue, it should get re-raised by the calls below. | |||||
pass | |||||
try: | |||||
os.rename(fname, self.sconsign) | |||||
except OSError: | |||||
# An OSError failure to rename may indicate something | |||||
# like the directory has no write permission, but | |||||
# the .sconsign file itself might still be writable, | |||||
# so try writing on top of it directly. An IOError | |||||
# here, or in any of the following calls, would get | |||||
# raised, indicating something like a potentially | |||||
# serious disk or network issue. | |||||
open(self.sconsign, 'wb').write(open(fname, 'rb').read()) | |||||
os.chmod(self.sconsign, mode) | |||||
try: | |||||
os.unlink(temp) | |||||
except (IOError, OSError): | |||||
pass | |||||
ForDirectory = DB | |||||
def File(name, dbm_module=None): | |||||
""" | |||||
Arrange for all signatures to be stored in a global .sconsign.db* | |||||
file. | |||||
""" | |||||
global ForDirectory, DB_Name, DB_Module | |||||
if name is None: | |||||
ForDirectory = DirFile | |||||
DB_Module = None | |||||
else: | |||||
ForDirectory = DB | |||||
DB_Name = name | |||||
if not dbm_module is None: | |||||
DB_Module = dbm_module | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,131 @@ | |||||
"""SCons.Scanner.C | |||||
This module implements the dependency scanner for C/C++ code. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/C.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Node.FS | |||||
import SCons.Scanner | |||||
import SCons.Util | |||||
import SCons.cpp | |||||
class SConsCPPScanner(SCons.cpp.PreProcessor): | |||||
""" | |||||
SCons-specific subclass of the cpp.py module's processing. | |||||
We subclass this so that: 1) we can deal with files represented | |||||
by Nodes, not strings; 2) we can keep track of the files that are | |||||
missing. | |||||
""" | |||||
def __init__(self, *args, **kw): | |||||
SCons.cpp.PreProcessor.__init__(self, *args, **kw) | |||||
self.missing = [] | |||||
def initialize_result(self, fname): | |||||
self.result = SCons.Util.UniqueList([fname]) | |||||
def finalize_result(self, fname): | |||||
return self.result[1:] | |||||
def find_include_file(self, t): | |||||
keyword, quote, fname = t | |||||
result = SCons.Node.FS.find_file(fname, self.searchpath[quote]) | |||||
if not result: | |||||
self.missing.append((fname, self.current_file)) | |||||
return result | |||||
def read_file(self, file): | |||||
try: | |||||
with open(str(file.rfile())) as fp: | |||||
return fp.read() | |||||
except EnvironmentError as e: | |||||
self.missing.append((file, self.current_file)) | |||||
return '' | |||||
def dictify_CPPDEFINES(env): | |||||
cppdefines = env.get('CPPDEFINES', {}) | |||||
if cppdefines is None: | |||||
return {} | |||||
if SCons.Util.is_Sequence(cppdefines): | |||||
result = {} | |||||
for c in cppdefines: | |||||
if SCons.Util.is_Sequence(c): | |||||
result[c[0]] = c[1] | |||||
else: | |||||
result[c] = None | |||||
return result | |||||
if not SCons.Util.is_Dict(cppdefines): | |||||
return {cppdefines : None} | |||||
return cppdefines | |||||
class SConsCPPScannerWrapper(object): | |||||
""" | |||||
The SCons wrapper around a cpp.py scanner. | |||||
This is the actual glue between the calling conventions of generic | |||||
SCons scanners, and the (subclass of) cpp.py class that knows how | |||||
to look for #include lines with reasonably real C-preprocessor-like | |||||
evaluation of #if/#ifdef/#else/#elif lines. | |||||
""" | |||||
def __init__(self, name, variable): | |||||
self.name = name | |||||
self.path = SCons.Scanner.FindPathDirs(variable) | |||||
def __call__(self, node, env, path = ()): | |||||
cpp = SConsCPPScanner(current = node.get_dir(), | |||||
cpppath = path, | |||||
dict = dictify_CPPDEFINES(env)) | |||||
result = cpp(node) | |||||
for included, includer in cpp.missing: | |||||
fmt = "No dependency generated for file: %s (included from: %s) -- file not found" | |||||
SCons.Warnings.warn(SCons.Warnings.DependencyWarning, | |||||
fmt % (included, includer)) | |||||
return result | |||||
def recurse_nodes(self, nodes): | |||||
return nodes | |||||
def select(self, node): | |||||
return self | |||||
def CScanner(): | |||||
"""Return a prototype Scanner instance for scanning source files | |||||
that use the C pre-processor""" | |||||
# Here's how we would (or might) use the CPP scanner code above that | |||||
# knows how to evaluate #if/#ifdef/#else/#elif lines when searching | |||||
# for #includes. This is commented out for now until we add the | |||||
# right configurability to let users pick between the scanners. | |||||
#return SConsCPPScannerWrapper("CScanner", "CPPPATH") | |||||
cs = SCons.Scanner.ClassicCPP("CScanner", | |||||
"$CPPSUFFIXES", | |||||
"CPPPATH", | |||||
'^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') | |||||
return cs | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,73 @@ | |||||
"""SCons.Scanner.D | |||||
Scanner for the Digital Mars "D" programming language. | |||||
Coded by Andy Friesen | |||||
17 Nov 2003 | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/D.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Scanner | |||||
def DScanner(): | |||||
"""Return a prototype Scanner instance for scanning D source files""" | |||||
ds = D() | |||||
return ds | |||||
class D(SCons.Scanner.Classic): | |||||
def __init__ (self): | |||||
SCons.Scanner.Classic.__init__ ( | |||||
self, | |||||
name = "DScanner", | |||||
suffixes = '$DSUFFIXES', | |||||
path_variable = 'DPATH', | |||||
regex = '(?:import\s+)([\w\s=,.]+)(?:\s*:[\s\w,=]+)?(?:;)' | |||||
) | |||||
def find_include(self, include, source_dir, path): | |||||
# translate dots (package separators) to slashes | |||||
inc = include.replace('.', '/') | |||||
i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) | |||||
if i is None: | |||||
i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path) | |||||
return i, include | |||||
def find_include_names(self, node): | |||||
includes = [] | |||||
for iii in self.cre.findall(node.get_text_contents()): | |||||
for jjj in iii.split(','): | |||||
kkk = jjj.split('=')[-1] | |||||
includes.append(kkk.strip()) | |||||
return includes | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,109 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Scanner/Dir.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Node.FS | |||||
import SCons.Scanner | |||||
def only_dirs(nodes): | |||||
is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir) | |||||
return [node for node in nodes if is_Dir(node)] | |||||
def DirScanner(**kw): | |||||
"""Return a prototype Scanner instance for scanning | |||||
directories for on-disk files""" | |||||
kw['node_factory'] = SCons.Node.FS.Entry | |||||
kw['recursive'] = only_dirs | |||||
return SCons.Scanner.Base(scan_on_disk, "DirScanner", **kw) | |||||
def DirEntryScanner(**kw): | |||||
"""Return a prototype Scanner instance for "scanning" | |||||
directory Nodes for their in-memory entries""" | |||||
kw['node_factory'] = SCons.Node.FS.Entry | |||||
kw['recursive'] = None | |||||
return SCons.Scanner.Base(scan_in_memory, "DirEntryScanner", **kw) | |||||
skip_entry = {} | |||||
skip_entry_list = [ | |||||
'.', | |||||
'..', | |||||
'.sconsign', | |||||
# Used by the native dblite.py module. | |||||
'.sconsign.dblite', | |||||
# Used by dbm and dumbdbm. | |||||
'.sconsign.dir', | |||||
# Used by dbm. | |||||
'.sconsign.pag', | |||||
# Used by dumbdbm. | |||||
'.sconsign.dat', | |||||
'.sconsign.bak', | |||||
# Used by some dbm emulations using Berkeley DB. | |||||
'.sconsign.db', | |||||
] | |||||
for skip in skip_entry_list: | |||||
skip_entry[skip] = 1 | |||||
skip_entry[SCons.Node.FS._my_normcase(skip)] = 1 | |||||
do_not_scan = lambda k: k not in skip_entry | |||||
def scan_on_disk(node, env, path=()): | |||||
""" | |||||
Scans a directory for on-disk files and directories therein. | |||||
Looking up the entries will add these to the in-memory Node tree | |||||
representation of the file system, so all we have to do is just | |||||
that and then call the in-memory scanning function. | |||||
""" | |||||
try: | |||||
flist = node.fs.listdir(node.get_abspath()) | |||||
except (IOError, OSError): | |||||
return [] | |||||
e = node.Entry | |||||
for f in filter(do_not_scan, flist): | |||||
# Add ./ to the beginning of the file name so if it begins with a | |||||
# '#' we don't look it up relative to the top-level directory. | |||||
e('./' + f) | |||||
return scan_in_memory(node, env, path) | |||||
def scan_in_memory(node, env, path=()): | |||||
""" | |||||
"Scans" a Node.FS.Dir for its in-memory entries. | |||||
""" | |||||
try: | |||||
entries = node.entries | |||||
except AttributeError: | |||||
# It's not a Node.FS.Dir (or doesn't look enough like one for | |||||
# our purposes), which can happen if a target list containing | |||||
# mixed Node types (Dirs and Files, for example) has a Dir as | |||||
# the first entry. | |||||
return [] | |||||
entry_list = sorted(filter(do_not_scan, list(entries.keys()))) | |||||
return [entries[n] for n in entry_list] | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,316 @@ | |||||
"""SCons.Scanner.Fortran | |||||
This module implements the dependency scanner for Fortran code. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Scanner/Fortran.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import re | |||||
import SCons.Node | |||||
import SCons.Node.FS | |||||
import SCons.Scanner | |||||
import SCons.Util | |||||
import SCons.Warnings | |||||
class F90Scanner(SCons.Scanner.Classic): | |||||
""" | |||||
A Classic Scanner subclass for Fortran source files which takes | |||||
into account both USE and INCLUDE statements. This scanner will | |||||
work for both F77 and F90 (and beyond) compilers. | |||||
Currently, this scanner assumes that the include files do not contain | |||||
USE statements. To enable the ability to deal with USE statements | |||||
in include files, add logic right after the module names are found | |||||
to loop over each include file, search for and locate each USE | |||||
statement, and append each module name to the list of dependencies. | |||||
Caching the search results in a common dictionary somewhere so that | |||||
the same include file is not searched multiple times would be a | |||||
smart thing to do. | |||||
""" | |||||
def __init__(self, name, suffixes, path_variable, | |||||
use_regex, incl_regex, def_regex, *args, **kw): | |||||
self.cre_use = re.compile(use_regex, re.M) | |||||
self.cre_incl = re.compile(incl_regex, re.M) | |||||
self.cre_def = re.compile(def_regex, re.M) | |||||
def _scan(node, env, path, self=self): | |||||
node = node.rfile() | |||||
if not node.exists(): | |||||
return [] | |||||
return self.scan(node, env, path) | |||||
kw['function'] = _scan | |||||
kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) | |||||
kw['recursive'] = 1 | |||||
kw['skeys'] = suffixes | |||||
kw['name'] = name | |||||
SCons.Scanner.Current.__init__(self, *args, **kw) | |||||
def scan(self, node, env, path=()): | |||||
# cache the includes list in node so we only scan it once: | |||||
if node.includes != None: | |||||
mods_and_includes = node.includes | |||||
else: | |||||
# retrieve all included filenames | |||||
includes = self.cre_incl.findall(node.get_text_contents()) | |||||
# retrieve all USE'd module names | |||||
modules = self.cre_use.findall(node.get_text_contents()) | |||||
# retrieve all defined module names | |||||
defmodules = self.cre_def.findall(node.get_text_contents()) | |||||
# Remove all USE'd module names that are defined in the same file | |||||
# (case-insensitively) | |||||
d = {} | |||||
for m in defmodules: | |||||
d[m.lower()] = 1 | |||||
modules = [m for m in modules if m.lower() not in d] | |||||
# Convert module name to a .mod filename | |||||
suffix = env.subst('$FORTRANMODSUFFIX') | |||||
modules = [x.lower() + suffix for x in modules] | |||||
# Remove unique items from the list | |||||
mods_and_includes = SCons.Util.unique(includes+modules) | |||||
node.includes = mods_and_includes | |||||
# This is a hand-coded DSU (decorate-sort-undecorate, or | |||||
# Schwartzian transform) pattern. The sort key is the raw name | |||||
# of the file as specifed on the USE or INCLUDE line, which lets | |||||
# us keep the sort order constant regardless of whether the file | |||||
# is actually found in a Repository or locally. | |||||
nodes = [] | |||||
source_dir = node.get_dir() | |||||
if callable(path): | |||||
path = path() | |||||
for dep in mods_and_includes: | |||||
n, i = self.find_include(dep, source_dir, path) | |||||
if n is None: | |||||
SCons.Warnings.warn(SCons.Warnings.DependencyWarning, | |||||
"No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node)) | |||||
else: | |||||
sortkey = self.sort_key(dep) | |||||
nodes.append((sortkey, n)) | |||||
return [pair[1] for pair in sorted(nodes)] | |||||
def FortranScan(path_variable="FORTRANPATH"): | |||||
"""Return a prototype Scanner instance for scanning source files | |||||
for Fortran USE & INCLUDE statements""" | |||||
# The USE statement regex matches the following: | |||||
# | |||||
# USE module_name | |||||
# USE :: module_name | |||||
# USE, INTRINSIC :: module_name | |||||
# USE, NON_INTRINSIC :: module_name | |||||
# | |||||
# Limitations | |||||
# | |||||
# -- While the regex can handle multiple USE statements on one line, | |||||
# it cannot properly handle them if they are commented out. | |||||
# In either of the following cases: | |||||
# | |||||
# ! USE mod_a ; USE mod_b [entire line is commented out] | |||||
# USE mod_a ! ; USE mod_b [in-line comment of second USE statement] | |||||
# | |||||
# the second module name (mod_b) will be picked up as a dependency | |||||
# even though it should be ignored. The only way I can see | |||||
# to rectify this would be to modify the scanner to eliminate | |||||
# the call to re.findall, read in the contents of the file, | |||||
# treating the comment character as an end-of-line character | |||||
# in addition to the normal linefeed, loop over each line, | |||||
# weeding out the comments, and looking for the USE statements. | |||||
# One advantage to this is that the regex passed to the scanner | |||||
# would no longer need to match a semicolon. | |||||
# | |||||
# -- I question whether or not we need to detect dependencies to | |||||
# INTRINSIC modules because these are built-in to the compiler. | |||||
# If we consider them a dependency, will SCons look for them, not | |||||
# find them, and kill the build? Or will we there be standard | |||||
# compiler-specific directories we will need to point to so the | |||||
# compiler and SCons can locate the proper object and mod files? | |||||
# Here is a breakdown of the regex: | |||||
# | |||||
# (?i) : regex is case insensitive | |||||
# ^ : start of line | |||||
# (?: : group a collection of regex symbols without saving the match as a "group" | |||||
# ^|; : matches either the start of the line or a semicolon - semicolon | |||||
# ) : end the unsaved grouping | |||||
# \s* : any amount of white space | |||||
# USE : match the string USE, case insensitive | |||||
# (?: : group a collection of regex symbols without saving the match as a "group" | |||||
# \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols) | |||||
# (?: : group a collection of regex symbols without saving the match as a "group" | |||||
# (?: : establish another unsaved grouping of regex symbols | |||||
# \s* : any amount of white space | |||||
# , : match a comma | |||||
# \s* : any amount of white space | |||||
# (?:NON_)? : optionally match the prefix NON_, case insensitive | |||||
# INTRINSIC : match the string INTRINSIC, case insensitive | |||||
# )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression | |||||
# \s* : any amount of white space | |||||
# :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute | |||||
# ) : end the unsaved grouping | |||||
# ) : end the unsaved grouping | |||||
# \s* : match any amount of white space | |||||
# (\w+) : match the module name that is being USE'd | |||||
# | |||||
# | |||||
use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" | |||||
# The INCLUDE statement regex matches the following: | |||||
# | |||||
# INCLUDE 'some_Text' | |||||
# INCLUDE "some_Text" | |||||
# INCLUDE "some_Text" ; INCLUDE "some_Text" | |||||
# INCLUDE kind_"some_Text" | |||||
# INCLUDE kind_'some_Text" | |||||
# | |||||
# where some_Text can include any alphanumeric and/or special character | |||||
# as defined by the Fortran 2003 standard. | |||||
# | |||||
# Limitations: | |||||
# | |||||
# -- The Fortran standard dictates that a " or ' in the INCLUDE'd | |||||
# string must be represented as a "" or '', if the quotes that wrap | |||||
# the entire string are either a ' or ", respectively. While the | |||||
# regular expression below can detect the ' or " characters just fine, | |||||
# the scanning logic, presently is unable to detect them and reduce | |||||
# them to a single instance. This probably isn't an issue since, | |||||
# in practice, ' or " are not generally used in filenames. | |||||
# | |||||
# -- This regex will not properly deal with multiple INCLUDE statements | |||||
# when the entire line has been commented out, ala | |||||
# | |||||
# ! INCLUDE 'some_file' ; INCLUDE 'some_file' | |||||
# | |||||
# In such cases, it will properly ignore the first INCLUDE file, | |||||
# but will actually still pick up the second. Interestingly enough, | |||||
# the regex will properly deal with these cases: | |||||
# | |||||
# INCLUDE 'some_file' | |||||
# INCLUDE 'some_file' !; INCLUDE 'some_file' | |||||
# | |||||
# To get around the above limitation, the FORTRAN programmer could | |||||
# simply comment each INCLUDE statement separately, like this | |||||
# | |||||
# ! INCLUDE 'some_file' !; INCLUDE 'some_file' | |||||
# | |||||
# The way I see it, the only way to get around this limitation would | |||||
# be to modify the scanning logic to replace the calls to re.findall | |||||
# with a custom loop that processes each line separately, throwing | |||||
# away fully commented out lines before attempting to match against | |||||
# the INCLUDE syntax. | |||||
# | |||||
# Here is a breakdown of the regex: | |||||
# | |||||
# (?i) : regex is case insensitive | |||||
# (?: : begin a non-saving group that matches the following: | |||||
# ^ : either the start of the line | |||||
# | : or | |||||
# ['">]\s*; : a semicolon that follows a single quote, | |||||
# double quote or greater than symbol (with any | |||||
# amount of whitespace in between). This will | |||||
# allow the regex to match multiple INCLUDE | |||||
# statements per line (although it also requires | |||||
# the positive lookahead assertion that is | |||||
# used below). It will even properly deal with | |||||
# (i.e. ignore) cases in which the additional | |||||
# INCLUDES are part of an in-line comment, ala | |||||
# " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' " | |||||
# ) : end of non-saving group | |||||
# \s* : any amount of white space | |||||
# INCLUDE : match the string INCLUDE, case insensitive | |||||
# \s+ : match one or more white space characters | |||||
# (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard | |||||
# [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol | |||||
# (.+?) : match one or more characters that make up | |||||
# the included path and file name and save it | |||||
# in a group. The Fortran standard allows for | |||||
# any non-control character to be used. The dot | |||||
# operator will pick up any character, including | |||||
# control codes, but I can't conceive of anyone | |||||
# putting control codes in their file names. | |||||
# The question mark indicates it is non-greedy so | |||||
# that regex will match only up to the next quote, | |||||
# double quote, or greater than symbol | |||||
# (?=["'>]) : positive lookahead assertion to match the include | |||||
# delimiter - an apostrophe, double quote, or | |||||
# greater than symbol. This level of complexity | |||||
# is required so that the include delimiter is | |||||
# not consumed by the match, thus allowing the | |||||
# sub-regex discussed above to uniquely match a | |||||
# set of semicolon-separated INCLUDE statements | |||||
# (as allowed by the F2003 standard) | |||||
include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" | |||||
# The MODULE statement regex finds module definitions by matching | |||||
# the following: | |||||
# | |||||
# MODULE module_name | |||||
# | |||||
# but *not* the following: | |||||
# | |||||
# MODULE PROCEDURE procedure_name | |||||
# | |||||
# Here is a breakdown of the regex: | |||||
# | |||||
# (?i) : regex is case insensitive | |||||
# ^\s* : any amount of white space | |||||
# MODULE : match the string MODULE, case insensitive | |||||
# \s+ : match one or more white space characters | |||||
# (?!PROCEDURE) : but *don't* match if the next word matches | |||||
# PROCEDURE (negative lookahead assertion), | |||||
# case insensitive | |||||
# (\w+) : match one or more alphanumeric characters | |||||
# that make up the defined module name and | |||||
# save it in a group | |||||
def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" | |||||
scanner = F90Scanner("FortranScan", | |||||
"$FORTRANSUFFIXES", | |||||
path_variable, | |||||
use_regex, | |||||
include_regex, | |||||
def_regex) | |||||
return scanner | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,48 @@ | |||||
"""SCons.Scanner.IDL | |||||
This module implements the dependency scanner for IDL (Interface | |||||
Definition Language) files. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/IDL.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Node.FS | |||||
import SCons.Scanner | |||||
def IDLScan(): | |||||
"""Return a prototype Scanner instance for scanning IDL source files""" | |||||
cs = SCons.Scanner.ClassicCPP("IDLScan", | |||||
"$IDLSUFFIXES", | |||||
"CPPPATH", | |||||
'^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")') | |||||
return cs | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,437 @@ | |||||
"""SCons.Scanner.LaTeX | |||||
This module implements the dependency scanner for LaTeX code. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/LaTeX.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os.path | |||||
import re | |||||
import SCons.Scanner | |||||
import SCons.Util | |||||
# list of graphics file extensions for TeX and LaTeX | |||||
TexGraphics = ['.eps', '.ps'] | |||||
#LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] | |||||
LatexGraphics = [ '.png', '.jpg', '.gif', '.tif'] | |||||
# Used as a return value of modify_env_var if the variable is not set. | |||||
class _Null(object): | |||||
pass | |||||
_null = _Null | |||||
# The user specifies the paths in env[variable], similar to other builders. | |||||
# They may be relative and must be converted to absolute, as expected | |||||
# by LaTeX and Co. The environment may already have some paths in | |||||
# env['ENV'][var]. These paths are honored, but the env[var] paths have | |||||
# higher precedence. All changes are un-done on exit. | |||||
def modify_env_var(env, var, abspath): | |||||
try: | |||||
save = env['ENV'][var] | |||||
except KeyError: | |||||
save = _null | |||||
env.PrependENVPath(var, abspath) | |||||
try: | |||||
if SCons.Util.is_List(env[var]): | |||||
env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]]) | |||||
else: | |||||
# Split at os.pathsep to convert into absolute path | |||||
env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)]) | |||||
except KeyError: | |||||
pass | |||||
# Convert into a string explicitly to append ":" (without which it won't search system | |||||
# paths as well). The problem is that env.AppendENVPath(var, ":") | |||||
# does not work, refuses to append ":" (os.pathsep). | |||||
if SCons.Util.is_List(env['ENV'][var]): | |||||
env['ENV'][var] = os.pathsep.join(env['ENV'][var]) | |||||
# Append the trailing os.pathsep character here to catch the case with no env[var] | |||||
env['ENV'][var] = env['ENV'][var] + os.pathsep | |||||
return save | |||||
class FindENVPathDirs(object): | |||||
""" | |||||
A class to bind a specific E{*}PATH variable name to a function that | |||||
will return all of the E{*}path directories. | |||||
""" | |||||
def __init__(self, variable): | |||||
self.variable = variable | |||||
def __call__(self, env, dir=None, target=None, source=None, argument=None): | |||||
import SCons.PathList | |||||
try: | |||||
path = env['ENV'][self.variable] | |||||
except KeyError: | |||||
return () | |||||
dir = dir or env.fs._cwd | |||||
path = SCons.PathList.PathList(path).subst_path(env, target, source) | |||||
return tuple(dir.Rfindalldirs(path)) | |||||
def LaTeXScanner(): | |||||
""" | |||||
Return a prototype Scanner instance for scanning LaTeX source files | |||||
when built with latex. | |||||
""" | |||||
ds = LaTeX(name = "LaTeXScanner", | |||||
suffixes = '$LATEXSUFFIXES', | |||||
# in the search order, see below in LaTeX class docstring | |||||
graphics_extensions = TexGraphics, | |||||
recursive = 0) | |||||
return ds | |||||
def PDFLaTeXScanner(): | |||||
""" | |||||
Return a prototype Scanner instance for scanning LaTeX source files | |||||
when built with pdflatex. | |||||
""" | |||||
ds = LaTeX(name = "PDFLaTeXScanner", | |||||
suffixes = '$LATEXSUFFIXES', | |||||
# in the search order, see below in LaTeX class docstring | |||||
graphics_extensions = LatexGraphics, | |||||
recursive = 0) | |||||
return ds | |||||
class LaTeX(SCons.Scanner.Base): | |||||
""" | |||||
Class for scanning LaTeX files for included files. | |||||
Unlike most scanners, which use regular expressions that just | |||||
return the included file name, this returns a tuple consisting | |||||
of the keyword for the inclusion ("include", "includegraphics", | |||||
"input", or "bibliography"), and then the file name itself. | |||||
Based on a quick look at LaTeX documentation, it seems that we | |||||
should append .tex suffix for the "include" keywords, append .tex if | |||||
there is no extension for the "input" keyword, and need to add .bib | |||||
for the "bibliography" keyword that does not accept extensions by itself. | |||||
Finally, if there is no extension for an "includegraphics" keyword | |||||
latex will append .ps or .eps to find the file, while pdftex may use .pdf, | |||||
.jpg, .tif, .mps, or .png. | |||||
The actual subset and search order may be altered by | |||||
DeclareGraphicsExtensions command. This complication is ignored. | |||||
The default order corresponds to experimentation with teTeX:: | |||||
$ latex --version | |||||
pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4) | |||||
kpathsea version 3.5.4 | |||||
The order is: | |||||
['.eps', '.ps'] for latex | |||||
['.png', '.pdf', '.jpg', '.tif']. | |||||
Another difference is that the search path is determined by the type | |||||
of the file being searched: | |||||
env['TEXINPUTS'] for "input" and "include" keywords | |||||
env['TEXINPUTS'] for "includegraphics" keyword | |||||
env['TEXINPUTS'] for "lstinputlisting" keyword | |||||
env['BIBINPUTS'] for "bibliography" keyword | |||||
env['BSTINPUTS'] for "bibliographystyle" keyword | |||||
env['INDEXSTYLE'] for "makeindex" keyword, no scanning support needed just allows user to set it if needed. | |||||
FIXME: also look for the class or style in document[class|style]{} | |||||
FIXME: also look for the argument of bibliographystyle{} | |||||
""" | |||||
keyword_paths = {'include': 'TEXINPUTS', | |||||
'input': 'TEXINPUTS', | |||||
'includegraphics': 'TEXINPUTS', | |||||
'bibliography': 'BIBINPUTS', | |||||
'bibliographystyle': 'BSTINPUTS', | |||||
'addbibresource': 'BIBINPUTS', | |||||
'addglobalbib': 'BIBINPUTS', | |||||
'addsectionbib': 'BIBINPUTS', | |||||
'makeindex': 'INDEXSTYLE', | |||||
'usepackage': 'TEXINPUTS', | |||||
'lstinputlisting': 'TEXINPUTS'} | |||||
env_variables = SCons.Util.unique(list(keyword_paths.values())) | |||||
two_arg_commands = ['import', 'subimport', | |||||
'includefrom', 'subincludefrom', | |||||
'inputfrom', 'subinputfrom'] | |||||
def __init__(self, name, suffixes, graphics_extensions, *args, **kw): | |||||
# We have to include \n with the % we exclude from the first part | |||||
# part of the regex because the expression is compiled with re.M. | |||||
# Without the \n, the ^ could match the beginning of a *previous* | |||||
# line followed by one or more newline characters (i.e. blank | |||||
# lines), interfering with a match on the next line. | |||||
# add option for whitespace before the '[options]' or the '{filename}' | |||||
regex = r''' | |||||
^[^%\n]* | |||||
\\( | |||||
include | |||||
| includegraphics(?:\s*\[[^\]]+\])? | |||||
| lstinputlisting(?:\[[^\]]+\])? | |||||
| input | |||||
| import | |||||
| subimport | |||||
| includefrom | |||||
| subincludefrom | |||||
| inputfrom | |||||
| subinputfrom | |||||
| bibliography | |||||
| addbibresource | |||||
| addglobalbib | |||||
| addsectionbib | |||||
| usepackage | |||||
) | |||||
\s*{([^}]*)} # first arg | |||||
(?: \s*{([^}]*)} )? # maybe another arg | |||||
''' | |||||
self.cre = re.compile(regex, re.M | re.X) | |||||
self.comment_re = re.compile(r'^((?:(?:\\%)|[^%\n])*)(.*)$', re.M) | |||||
self.graphics_extensions = graphics_extensions | |||||
def _scan(node, env, path=(), self=self): | |||||
node = node.rfile() | |||||
if not node.exists(): | |||||
return [] | |||||
return self.scan_recurse(node, path) | |||||
class FindMultiPathDirs(object): | |||||
"""The stock FindPathDirs function has the wrong granularity: | |||||
it is called once per target, while we need the path that depends | |||||
on what kind of included files is being searched. This wrapper | |||||
hides multiple instances of FindPathDirs, one per the LaTeX path | |||||
variable in the environment. When invoked, the function calculates | |||||
and returns all the required paths as a dictionary (converted into | |||||
a tuple to become hashable). Then the scan function converts it | |||||
back and uses a dictionary of tuples rather than a single tuple | |||||
of paths. | |||||
""" | |||||
def __init__(self, dictionary): | |||||
self.dictionary = {} | |||||
for k,n in dictionary.items(): | |||||
self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), | |||||
FindENVPathDirs(n) ) | |||||
def __call__(self, env, dir=None, target=None, source=None, | |||||
argument=None): | |||||
di = {} | |||||
for k,(c,cENV) in self.dictionary.items(): | |||||
di[k] = ( c(env, dir=None, target=None, source=None, | |||||
argument=None) , | |||||
cENV(env, dir=None, target=None, source=None, | |||||
argument=None) ) | |||||
# To prevent "dict is not hashable error" | |||||
return tuple(di.items()) | |||||
class LaTeXScanCheck(object): | |||||
"""Skip all but LaTeX source files, i.e., do not scan *.eps, | |||||
*.pdf, *.jpg, etc. | |||||
""" | |||||
def __init__(self, suffixes): | |||||
self.suffixes = suffixes | |||||
def __call__(self, node, env): | |||||
current = not node.has_builder() or node.is_up_to_date() | |||||
scannable = node.get_suffix() in env.subst_list(self.suffixes)[0] | |||||
# Returning false means that the file is not scanned. | |||||
return scannable and current | |||||
kw['function'] = _scan | |||||
kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) | |||||
kw['recursive'] = 0 | |||||
kw['skeys'] = suffixes | |||||
kw['scan_check'] = LaTeXScanCheck(suffixes) | |||||
kw['name'] = name | |||||
SCons.Scanner.Base.__init__(self, *args, **kw) | |||||
def _latex_names(self, include_type, filename): | |||||
if include_type == 'input': | |||||
base, ext = os.path.splitext( filename ) | |||||
if ext == "": | |||||
return [filename + '.tex'] | |||||
if include_type in ('include', 'import', 'subimport', | |||||
'includefrom', 'subincludefrom', | |||||
'inputfrom', 'subinputfrom'): | |||||
base, ext = os.path.splitext( filename ) | |||||
if ext == "": | |||||
return [filename + '.tex'] | |||||
if include_type == 'bibliography': | |||||
base, ext = os.path.splitext( filename ) | |||||
if ext == "": | |||||
return [filename + '.bib'] | |||||
if include_type == 'usepackage': | |||||
base, ext = os.path.splitext( filename ) | |||||
if ext == "": | |||||
return [filename + '.sty'] | |||||
if include_type == 'includegraphics': | |||||
base, ext = os.path.splitext( filename ) | |||||
if ext == "": | |||||
#return [filename+e for e in self.graphics_extensions + TexGraphics] | |||||
# use the line above to find dependencies for the PDF builder | |||||
# when only an .eps figure is present. Since it will be found | |||||
# if the user tells scons how to make the pdf figure, leave | |||||
# it out for now. | |||||
return [filename+e for e in self.graphics_extensions] | |||||
return [filename] | |||||
def sort_key(self, include): | |||||
return SCons.Node.FS._my_normcase(str(include)) | |||||
def find_include(self, include, source_dir, path): | |||||
inc_type, inc_subdir, inc_filename = include | |||||
try: | |||||
sub_paths = path[inc_type] | |||||
except (IndexError, KeyError): | |||||
sub_paths = ((), ()) | |||||
try_names = self._latex_names(inc_type, inc_filename) | |||||
# There are three search paths to try: | |||||
# 1. current directory "source_dir" | |||||
# 2. env[var] | |||||
# 3. env['ENV'][var] | |||||
search_paths = [(source_dir,)] + list(sub_paths) | |||||
for n in try_names: | |||||
for search_path in search_paths: | |||||
paths = tuple([d.Dir(inc_subdir) for d in search_path]) | |||||
i = SCons.Node.FS.find_file(n, paths) | |||||
if i: | |||||
return i, include | |||||
return None, include | |||||
def canonical_text(self, text): | |||||
"""Standardize an input TeX-file contents. | |||||
Currently: | |||||
* removes comments, unwrapping comment-wrapped lines. | |||||
""" | |||||
out = [] | |||||
line_continues_a_comment = False | |||||
for line in text.splitlines(): | |||||
line,comment = self.comment_re.findall(line)[0] | |||||
if line_continues_a_comment == True: | |||||
out[-1] = out[-1] + line.lstrip() | |||||
else: | |||||
out.append(line) | |||||
line_continues_a_comment = len(comment) > 0 | |||||
return '\n'.join(out).rstrip()+'\n' | |||||
def scan(self, node, subdir='.'): | |||||
# Modify the default scan function to allow for the regular | |||||
# expression to return a comma separated list of file names | |||||
# as can be the case with the bibliography keyword. | |||||
# Cache the includes list in node so we only scan it once: | |||||
# path_dict = dict(list(path)) | |||||
# add option for whitespace (\s) before the '[' | |||||
noopt_cre = re.compile('\s*\[.*$') | |||||
if node.includes != None: | |||||
includes = node.includes | |||||
else: | |||||
text = self.canonical_text(node.get_text_contents()) | |||||
includes = self.cre.findall(text) | |||||
# 1. Split comma-separated lines, e.g. | |||||
# ('bibliography', 'phys,comp') | |||||
# should become two entries | |||||
# ('bibliography', 'phys') | |||||
# ('bibliography', 'comp') | |||||
# 2. Remove the options, e.g., such as | |||||
# ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps') | |||||
# should become | |||||
# ('includegraphics', 'picture.eps') | |||||
split_includes = [] | |||||
for include in includes: | |||||
inc_type = noopt_cre.sub('', include[0]) | |||||
inc_subdir = subdir | |||||
if inc_type in self.two_arg_commands: | |||||
inc_subdir = os.path.join(subdir, include[1]) | |||||
inc_list = include[2].split(',') | |||||
else: | |||||
inc_list = include[1].split(',') | |||||
for j in range(len(inc_list)): | |||||
split_includes.append( (inc_type, inc_subdir, inc_list[j]) ) | |||||
# | |||||
includes = split_includes | |||||
node.includes = includes | |||||
return includes | |||||
def scan_recurse(self, node, path=()): | |||||
""" do a recursive scan of the top level target file | |||||
This lets us search for included files based on the | |||||
directory of the main file just as latex does""" | |||||
path_dict = dict(list(path)) | |||||
queue = [] | |||||
queue.extend( self.scan(node) ) | |||||
seen = {} | |||||
# This is a hand-coded DSU (decorate-sort-undecorate, or | |||||
# Schwartzian transform) pattern. The sort key is the raw name | |||||
# of the file as specifed on the \include, \input, etc. line. | |||||
# TODO: what about the comment in the original Classic scanner: | |||||
# """which lets | |||||
# us keep the sort order constant regardless of whether the file | |||||
# is actually found in a Repository or locally.""" | |||||
nodes = [] | |||||
source_dir = node.get_dir() | |||||
#for include in includes: | |||||
while queue: | |||||
include = queue.pop() | |||||
inc_type, inc_subdir, inc_filename = include | |||||
try: | |||||
if seen[inc_filename] == 1: | |||||
continue | |||||
except KeyError: | |||||
seen[inc_filename] = 1 | |||||
# | |||||
# Handle multiple filenames in include[1] | |||||
# | |||||
n, i = self.find_include(include, source_dir, path_dict) | |||||
if n is None: | |||||
# Do not bother with 'usepackage' warnings, as they most | |||||
# likely refer to system-level files | |||||
if inc_type != 'usepackage': | |||||
SCons.Warnings.warn(SCons.Warnings.DependencyWarning, | |||||
"No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) | |||||
else: | |||||
sortkey = self.sort_key(n) | |||||
nodes.append((sortkey, n)) | |||||
# recurse down | |||||
queue.extend( self.scan(n, inc_subdir) ) | |||||
return [pair[1] for pair in sorted(nodes)] | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,116 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/Prog.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Node | |||||
import SCons.Node.FS | |||||
import SCons.Scanner | |||||
import SCons.Util | |||||
# global, set by --debug=findlibs | |||||
print_find_libs = None | |||||
def ProgramScanner(**kw): | |||||
"""Return a prototype Scanner instance for scanning executable | |||||
files for static-lib dependencies""" | |||||
kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH') | |||||
ps = SCons.Scanner.Base(scan, "ProgramScanner", **kw) | |||||
return ps | |||||
def _subst_libs(env, libs): | |||||
""" | |||||
Substitute environment variables and split into list. | |||||
""" | |||||
if SCons.Util.is_String(libs): | |||||
libs = env.subst(libs) | |||||
if SCons.Util.is_String(libs): | |||||
libs = libs.split() | |||||
elif SCons.Util.is_Sequence(libs): | |||||
_libs = [] | |||||
for l in libs: | |||||
_libs += _subst_libs(env, l) | |||||
libs = _libs | |||||
else: | |||||
# libs is an object (Node, for example) | |||||
libs = [libs] | |||||
return libs | |||||
def scan(node, env, libpath = ()): | |||||
""" | |||||
This scanner scans program files for static-library | |||||
dependencies. It will search the LIBPATH environment variable | |||||
for libraries specified in the LIBS variable, returning any | |||||
files it finds as dependencies. | |||||
""" | |||||
try: | |||||
libs = env['LIBS'] | |||||
except KeyError: | |||||
# There are no LIBS in this environment, so just return a null list: | |||||
return [] | |||||
libs = _subst_libs(env, libs) | |||||
try: | |||||
prefix = env['LIBPREFIXES'] | |||||
if not SCons.Util.is_List(prefix): | |||||
prefix = [ prefix ] | |||||
except KeyError: | |||||
prefix = [ '' ] | |||||
try: | |||||
suffix = env['LIBSUFFIXES'] | |||||
if not SCons.Util.is_List(suffix): | |||||
suffix = [ suffix ] | |||||
except KeyError: | |||||
suffix = [ '' ] | |||||
pairs = [] | |||||
for suf in map(env.subst, suffix): | |||||
for pref in map(env.subst, prefix): | |||||
pairs.append((pref, suf)) | |||||
result = [] | |||||
if callable(libpath): | |||||
libpath = libpath() | |||||
find_file = SCons.Node.FS.find_file | |||||
adjustixes = SCons.Util.adjustixes | |||||
for lib in libs: | |||||
if SCons.Util.is_String(lib): | |||||
for pref, suf in pairs: | |||||
l = adjustixes(lib, pref, suf) | |||||
l = find_file(l, libpath, verbose=print_find_libs) | |||||
if l: | |||||
result.append(l) | |||||
else: | |||||
result.append(lib) | |||||
return result | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,66 @@ | |||||
"""SCons.Scanner.RC | |||||
This module implements the dependency scanner for RC (Interface | |||||
Definition Language) files. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/RC.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import re | |||||
import SCons.Node.FS | |||||
import SCons.Scanner | |||||
def no_tlb(nodes): | |||||
""" | |||||
Filter out .tlb files as they are binary and shouldn't be scanned | |||||
""" | |||||
# print("Nodes:%s"%[str(n) for n in nodes]) | |||||
return [n for n in nodes if str(n)[-4:] != '.tlb'] | |||||
def RCScan(): | |||||
"""Return a prototype Scanner instance for scanning RC source files""" | |||||
res_re= r'^(?:\s*#\s*(?:include)|' \ | |||||
'.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ | |||||
'\s*.*?)' \ | |||||
'\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' | |||||
resScanner = SCons.Scanner.ClassicCPP("ResourceScanner", | |||||
"$RCSUFFIXES", | |||||
"CPPPATH", | |||||
res_re, | |||||
recursive=no_tlb) | |||||
return resScanner | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,45 @@ | |||||
"""SCons.Scanner.SWIG | |||||
This module implements the dependency scanner for SWIG code. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/SWIG.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Scanner | |||||
SWIGSuffixes = [ '.i' ] | |||||
def SWIGScanner(): | |||||
expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' | |||||
scanner = SCons.Scanner.ClassicCPP("SWIGScanner", ".i", "SWIGPATH", expr) | |||||
return scanner | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,421 @@ | |||||
"""SCons.Scanner | |||||
The Scanner package for the SCons software construction utility. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Scanner/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import re | |||||
import SCons.Node.FS | |||||
import SCons.Util | |||||
class _Null(object): | |||||
pass | |||||
# This is used instead of None as a default argument value so None can be | |||||
# used as an actual argument value. | |||||
_null = _Null | |||||
def Scanner(function, *args, **kw): | |||||
""" | |||||
Public interface factory function for creating different types | |||||
of Scanners based on the different types of "functions" that may | |||||
be supplied. | |||||
TODO: Deprecate this some day. We've moved the functionality | |||||
inside the Base class and really don't need this factory function | |||||
any more. It was, however, used by some of our Tool modules, so | |||||
the call probably ended up in various people's custom modules | |||||
patterned on SCons code. | |||||
""" | |||||
if SCons.Util.is_Dict(function): | |||||
return Selector(function, *args, **kw) | |||||
else: | |||||
return Base(function, *args, **kw) | |||||
class FindPathDirs(object): | |||||
""" | |||||
A class to bind a specific E{*}PATH variable name to a function that | |||||
will return all of the E{*}path directories. | |||||
""" | |||||
def __init__(self, variable): | |||||
self.variable = variable | |||||
def __call__(self, env, dir=None, target=None, source=None, argument=None): | |||||
import SCons.PathList | |||||
try: | |||||
path = env[self.variable] | |||||
except KeyError: | |||||
return () | |||||
dir = dir or env.fs._cwd | |||||
path = SCons.PathList.PathList(path).subst_path(env, target, source) | |||||
return tuple(dir.Rfindalldirs(path)) | |||||
class Base(object): | |||||
""" | |||||
The base class for dependency scanners. This implements | |||||
straightforward, single-pass scanning of a single file. | |||||
""" | |||||
def __init__(self, | |||||
function, | |||||
name = "NONE", | |||||
argument = _null, | |||||
skeys = _null, | |||||
path_function = None, | |||||
# Node.FS.Base so that, by default, it's okay for a | |||||
# scanner to return a Dir, File or Entry. | |||||
node_class = SCons.Node.FS.Base, | |||||
node_factory = None, | |||||
scan_check = None, | |||||
recursive = None): | |||||
""" | |||||
Construct a new scanner object given a scanner function. | |||||
'function' - a scanner function taking two or three | |||||
arguments and returning a list of strings. | |||||
'name' - a name for identifying this scanner object. | |||||
'argument' - an optional argument that, if specified, will be | |||||
passed to both the scanner function and the path_function. | |||||
'skeys' - an optional list argument that can be used to determine | |||||
which scanner should be used for a given Node. In the case of File | |||||
nodes, for example, the 'skeys' would be file suffixes. | |||||
'path_function' - a function that takes four or five arguments | |||||
(a construction environment, Node for the directory containing | |||||
the SConscript file that defined the primary target, list of | |||||
target nodes, list of source nodes, and optional argument for | |||||
this instance) and returns a tuple of the directories that can | |||||
be searched for implicit dependency files. May also return a | |||||
callable() which is called with no args and returns the tuple | |||||
(supporting Bindable class). | |||||
'node_class' - the class of Nodes which this scan will return. | |||||
If node_class is None, then this scanner will not enforce any | |||||
Node conversion and will return the raw results from the | |||||
underlying scanner function. | |||||
'node_factory' - the factory function to be called to translate | |||||
the raw results returned by the scanner function into the | |||||
expected node_class objects. | |||||
'scan_check' - a function to be called to first check whether | |||||
this node really needs to be scanned. | |||||
'recursive' - specifies that this scanner should be invoked | |||||
recursively on all of the implicit dependencies it returns | |||||
(the canonical example being #include lines in C source files). | |||||
May be a callable, which will be called to filter the list | |||||
of nodes found to select a subset for recursive scanning | |||||
(the canonical example being only recursively scanning | |||||
subdirectories within a directory). | |||||
The scanner function's first argument will be a Node that should | |||||
be scanned for dependencies, the second argument will be an | |||||
Environment object, the third argument will be the tuple of paths | |||||
returned by the path_function, and the fourth argument will be | |||||
the value passed into 'argument', and the returned list should | |||||
contain the Nodes for all the direct dependencies of the file. | |||||
Examples: | |||||
s = Scanner(my_scanner_function) | |||||
s = Scanner(function = my_scanner_function) | |||||
s = Scanner(function = my_scanner_function, argument = 'foo') | |||||
""" | |||||
# Note: this class could easily work with scanner functions that take | |||||
# something other than a filename as an argument (e.g. a database | |||||
# node) and a dependencies list that aren't file names. All that | |||||
# would need to be changed is the documentation. | |||||
self.function = function | |||||
self.path_function = path_function | |||||
self.name = name | |||||
self.argument = argument | |||||
if skeys is _null: | |||||
if SCons.Util.is_Dict(function): | |||||
skeys = list(function.keys()) | |||||
else: | |||||
skeys = [] | |||||
self.skeys = skeys | |||||
self.node_class = node_class | |||||
self.node_factory = node_factory | |||||
self.scan_check = scan_check | |||||
if callable(recursive): | |||||
self.recurse_nodes = recursive | |||||
elif recursive: | |||||
self.recurse_nodes = self._recurse_all_nodes | |||||
else: | |||||
self.recurse_nodes = self._recurse_no_nodes | |||||
def path(self, env, dir=None, target=None, source=None): | |||||
if not self.path_function: | |||||
return () | |||||
if self.argument is not _null: | |||||
return self.path_function(env, dir, target, source, self.argument) | |||||
else: | |||||
return self.path_function(env, dir, target, source) | |||||
def __call__(self, node, env, path=()): | |||||
""" | |||||
This method scans a single object. 'node' is the node | |||||
that will be passed to the scanner function, and 'env' is the | |||||
environment that will be passed to the scanner function. A list of | |||||
direct dependency nodes for the specified node will be returned. | |||||
""" | |||||
if self.scan_check and not self.scan_check(node, env): | |||||
return [] | |||||
self = self.select(node) | |||||
if not self.argument is _null: | |||||
node_list = self.function(node, env, path, self.argument) | |||||
else: | |||||
node_list = self.function(node, env, path) | |||||
kw = {} | |||||
if hasattr(node, 'dir'): | |||||
kw['directory'] = node.dir | |||||
node_factory = env.get_factory(self.node_factory) | |||||
nodes = [] | |||||
for l in node_list: | |||||
if self.node_class and not isinstance(l, self.node_class): | |||||
l = node_factory(l, **kw) | |||||
nodes.append(l) | |||||
return nodes | |||||
def __eq__(self, other): | |||||
try: | |||||
return self.__dict__ == other.__dict__ | |||||
except AttributeError: | |||||
# other probably doesn't have a __dict__ | |||||
return self.__dict__ == other | |||||
def __hash__(self): | |||||
return id(self) | |||||
def __str__(self): | |||||
return self.name | |||||
def add_skey(self, skey): | |||||
"""Add a skey to the list of skeys""" | |||||
self.skeys.append(skey) | |||||
def get_skeys(self, env=None): | |||||
if env and SCons.Util.is_String(self.skeys): | |||||
return env.subst_list(self.skeys)[0] | |||||
return self.skeys | |||||
def select(self, node): | |||||
if SCons.Util.is_Dict(self.function): | |||||
key = node.scanner_key() | |||||
try: | |||||
return self.function[key] | |||||
except KeyError: | |||||
return None | |||||
else: | |||||
return self | |||||
def _recurse_all_nodes(self, nodes): | |||||
return nodes | |||||
def _recurse_no_nodes(self, nodes): | |||||
return [] | |||||
# recurse_nodes = _recurse_no_nodes | |||||
def add_scanner(self, skey, scanner): | |||||
self.function[skey] = scanner | |||||
self.add_skey(skey) | |||||
class Selector(Base): | |||||
""" | |||||
A class for selecting a more specific scanner based on the | |||||
scanner_key() (suffix) for a specific Node. | |||||
TODO: This functionality has been moved into the inner workings of | |||||
the Base class, and this class will be deprecated at some point. | |||||
(It was never exposed directly as part of the public interface, | |||||
although it is used by the Scanner() factory function that was | |||||
used by various Tool modules and therefore was likely a template | |||||
for custom modules that may be out there.) | |||||
""" | |||||
def __init__(self, dict, *args, **kw): | |||||
Base.__init__(self, None, *args, **kw) | |||||
self.dict = dict | |||||
self.skeys = list(dict.keys()) | |||||
def __call__(self, node, env, path=()): | |||||
return self.select(node)(node, env, path) | |||||
def select(self, node): | |||||
try: | |||||
return self.dict[node.scanner_key()] | |||||
except KeyError: | |||||
return None | |||||
def add_scanner(self, skey, scanner): | |||||
self.dict[skey] = scanner | |||||
self.add_skey(skey) | |||||
class Current(Base): | |||||
""" | |||||
A class for scanning files that are source files (have no builder) | |||||
or are derived files and are current (which implies that they exist, | |||||
either locally or in a repository). | |||||
""" | |||||
def __init__(self, *args, **kw): | |||||
def current_check(node, env): | |||||
return not node.has_builder() or node.is_up_to_date() | |||||
kw['scan_check'] = current_check | |||||
Base.__init__(self, *args, **kw) | |||||
class Classic(Current): | |||||
""" | |||||
A Scanner subclass to contain the common logic for classic CPP-style | |||||
include scanning, but which can be customized to use different | |||||
regular expressions to find the includes. | |||||
Note that in order for this to work "out of the box" (without | |||||
overriding the find_include() and sort_key() methods), the regular | |||||
expression passed to the constructor must return the name of the | |||||
include file in group 0. | |||||
""" | |||||
def __init__(self, name, suffixes, path_variable, regex, *args, **kw): | |||||
self.cre = re.compile(regex, re.M) | |||||
def _scan(node, _, path=(), self=self): | |||||
node = node.rfile() | |||||
if not node.exists(): | |||||
return [] | |||||
return self.scan(node, path) | |||||
kw['function'] = _scan | |||||
kw['path_function'] = FindPathDirs(path_variable) | |||||
# Allow recursive to propagate if child class specifies. | |||||
# In this case resource scanner needs to specify a filter on which files | |||||
# get recursively processed. Previously was hardcoded to 1 instead of | |||||
# defaulted to 1. | |||||
kw['recursive'] = kw.get('recursive', 1) | |||||
kw['skeys'] = suffixes | |||||
kw['name'] = name | |||||
Current.__init__(self, *args, **kw) | |||||
def find_include(self, include, source_dir, path): | |||||
n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) | |||||
return n, include | |||||
def sort_key(self, include): | |||||
return SCons.Node.FS._my_normcase(include) | |||||
def find_include_names(self, node): | |||||
return self.cre.findall(node.get_text_contents()) | |||||
def scan(self, node, path=()): | |||||
# cache the includes list in node so we only scan it once: | |||||
if node.includes is not None: | |||||
includes = node.includes | |||||
else: | |||||
includes = self.find_include_names(node) | |||||
# Intern the names of the include files. Saves some memory | |||||
# if the same header is included many times. | |||||
node.includes = list(map(SCons.Util.silent_intern, includes)) | |||||
# This is a hand-coded DSU (decorate-sort-undecorate, or | |||||
# Schwartzian transform) pattern. The sort key is the raw name | |||||
# of the file as specifed on the #include line (including the | |||||
# " or <, since that may affect what file is found), which lets | |||||
# us keep the sort order constant regardless of whether the file | |||||
# is actually found in a Repository or locally. | |||||
nodes = [] | |||||
source_dir = node.get_dir() | |||||
if callable(path): | |||||
path = path() | |||||
for include in includes: | |||||
n, i = self.find_include(include, source_dir, path) | |||||
if n is None: | |||||
SCons.Warnings.warn(SCons.Warnings.DependencyWarning, | |||||
"No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) | |||||
else: | |||||
nodes.append((self.sort_key(include), n)) | |||||
return [pair[1] for pair in sorted(nodes)] | |||||
class ClassicCPP(Classic): | |||||
""" | |||||
A Classic Scanner subclass which takes into account the type of | |||||
bracketing used to include the file, and uses classic CPP rules | |||||
for searching for the files based on the bracketing. | |||||
Note that in order for this to work, the regular expression passed | |||||
to the constructor must return the leading bracket in group 0, and | |||||
the contained filename in group 1. | |||||
""" | |||||
def find_include(self, include, source_dir, path): | |||||
include = list(map(SCons.Util.to_str, include)) | |||||
if include[0] == '"': | |||||
paths = (source_dir,) + tuple(path) | |||||
else: | |||||
paths = tuple(path) + (source_dir,) | |||||
n = SCons.Node.FS.find_file(include[1], paths) | |||||
i = SCons.Util.silent_intern(include[1]) | |||||
return n, i | |||||
def sort_key(self, include): | |||||
return SCons.Node.FS._my_normcase(' '.join(include)) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,376 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
from __future__ import print_function | |||||
__revision__ = "src/engine/SCons/Script/Interactive.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """ | |||||
SCons interactive mode | |||||
""" | |||||
# TODO: | |||||
# | |||||
# This has the potential to grow into something with a really big life | |||||
# of its own, which might or might not be a good thing. Nevertheless, | |||||
# here are some enhancements that will probably be requested some day | |||||
# and are worth keeping in mind (assuming this takes off): | |||||
# | |||||
# - A command to re-read / re-load the SConscript files. This may | |||||
# involve allowing people to specify command-line options (e.g. -f, | |||||
# -I, --no-site-dir) that affect how the SConscript files are read. | |||||
# | |||||
# - Additional command-line options on the "build" command. | |||||
# | |||||
# Of the supported options that seemed to make sense (after a quick | |||||
# pass through the list), the ones that seemed likely enough to be | |||||
# used are listed in the man page and have explicit test scripts. | |||||
# | |||||
# These had code changed in Script/Main.py to support them, but didn't | |||||
# seem likely to be used regularly, so had no test scripts added: | |||||
# | |||||
# build --diskcheck=* | |||||
# build --implicit-cache=* | |||||
# build --implicit-deps-changed=* | |||||
# build --implicit-deps-unchanged=* | |||||
# | |||||
# These look like they should "just work" with no changes to the | |||||
# existing code, but like those above, look unlikely to be used and | |||||
# therefore had no test scripts added: | |||||
# | |||||
# build --random | |||||
# | |||||
# These I'm not sure about. They might be useful for individual | |||||
# "build" commands, and may even work, but they seem unlikely enough | |||||
# that we'll wait until they're requested before spending any time on | |||||
# writing test scripts for them, or investigating whether they work. | |||||
# | |||||
# build -q [??? is there a useful analog to the exit status?] | |||||
# build --duplicate= | |||||
# build --profile= | |||||
# build --max-drift= | |||||
# build --warn=* | |||||
# build --Y | |||||
# | |||||
# - Most of the SCons command-line options that the "build" command | |||||
# supports should be settable as default options that apply to all | |||||
# subsequent "build" commands. Maybe a "set {option}" command that | |||||
# maps to "SetOption('{option}')". | |||||
# | |||||
# - Need something in the 'help' command that prints the -h output. | |||||
# | |||||
# - A command to run the configure subsystem separately (must see how | |||||
# this interacts with the new automake model). | |||||
# | |||||
# - Command-line completion of target names; maybe even of SCons options? | |||||
# Completion is something that's supported by the Python cmd module, | |||||
# so this should be doable without too much trouble. | |||||
# | |||||
import cmd | |||||
import copy | |||||
import os | |||||
import re | |||||
import shlex | |||||
import sys | |||||
try: | |||||
import readline | |||||
except ImportError: | |||||
pass | |||||
class SConsInteractiveCmd(cmd.Cmd): | |||||
"""\ | |||||
build [TARGETS] Build the specified TARGETS and their dependencies. 'b' is a synonym. | |||||
clean [TARGETS] Clean (remove) the specified TARGETS and their dependencies. 'c' is a synonym. | |||||
exit Exit SCons interactive mode. | |||||
help [COMMAND] Prints help for the specified COMMAND. 'h' and '?' are synonyms. | |||||
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' are synonyms. | |||||
version Prints SCons version information. | |||||
""" | |||||
synonyms = { | |||||
'b' : 'build', | |||||
'c' : 'clean', | |||||
'h' : 'help', | |||||
'scons' : 'build', | |||||
'sh' : 'shell', | |||||
} | |||||
def __init__(self, **kw): | |||||
cmd.Cmd.__init__(self) | |||||
for key, val in kw.items(): | |||||
setattr(self, key, val) | |||||
if sys.platform == 'win32': | |||||
self.shell_variable = 'COMSPEC' | |||||
else: | |||||
self.shell_variable = 'SHELL' | |||||
def default(self, argv): | |||||
print("*** Unknown command: %s" % argv[0]) | |||||
def onecmd(self, line): | |||||
line = line.strip() | |||||
if not line: | |||||
print(self.lastcmd) | |||||
return self.emptyline() | |||||
self.lastcmd = line | |||||
if line[0] == '!': | |||||
line = 'shell ' + line[1:] | |||||
elif line[0] == '?': | |||||
line = 'help ' + line[1:] | |||||
if os.sep == '\\': | |||||
line = line.replace('\\', '\\\\') | |||||
argv = shlex.split(line) | |||||
argv[0] = self.synonyms.get(argv[0], argv[0]) | |||||
if not argv[0]: | |||||
return self.default(line) | |||||
else: | |||||
try: | |||||
func = getattr(self, 'do_' + argv[0]) | |||||
except AttributeError: | |||||
return self.default(argv) | |||||
return func(argv) | |||||
def do_build(self, argv): | |||||
"""\ | |||||
build [TARGETS] Build the specified TARGETS and their | |||||
dependencies. 'b' is a synonym. | |||||
""" | |||||
import SCons.Node | |||||
import SCons.SConsign | |||||
import SCons.Script.Main | |||||
options = copy.deepcopy(self.options) | |||||
options, targets = self.parser.parse_args(argv[1:], values=options) | |||||
SCons.Script.COMMAND_LINE_TARGETS = targets | |||||
if targets: | |||||
SCons.Script.BUILD_TARGETS = targets | |||||
else: | |||||
# If the user didn't specify any targets on the command line, | |||||
# use the list of default targets. | |||||
SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default | |||||
nodes = SCons.Script.Main._build_targets(self.fs, | |||||
options, | |||||
targets, | |||||
self.target_top) | |||||
if not nodes: | |||||
return | |||||
# Call each of the Node's alter_targets() methods, which may | |||||
# provide additional targets that ended up as part of the build | |||||
# (the canonical example being a VariantDir() when we're building | |||||
# from a source directory) and which we therefore need their | |||||
# state cleared, too. | |||||
x = [] | |||||
for n in nodes: | |||||
x.extend(n.alter_targets()[0]) | |||||
nodes.extend(x) | |||||
# Clean up so that we can perform the next build correctly. | |||||
# | |||||
# We do this by walking over all the children of the targets, | |||||
# and clearing their state. | |||||
# | |||||
# We currently have to re-scan each node to find their | |||||
# children, because built nodes have already been partially | |||||
# cleared and don't remember their children. (In scons | |||||
# 0.96.1 and earlier, this wasn't the case, and we didn't | |||||
# have to re-scan the nodes.) | |||||
# | |||||
# Because we have to re-scan each node, we can't clear the | |||||
# nodes as we walk over them, because we may end up rescanning | |||||
# a cleared node as we scan a later node. Therefore, only | |||||
# store the list of nodes that need to be cleared as we walk | |||||
# the tree, and clear them in a separate pass. | |||||
# | |||||
# XXX: Someone more familiar with the inner workings of scons | |||||
# may be able to point out a more efficient way to do this. | |||||
SCons.Script.Main.progress_display("scons: Clearing cached node information ...") | |||||
seen_nodes = {} | |||||
def get_unseen_children(node, parent, seen_nodes=seen_nodes): | |||||
def is_unseen(node, seen_nodes=seen_nodes): | |||||
return node not in seen_nodes | |||||
return [child for child in node.children(scan=1) if is_unseen(child)] | |||||
def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes): | |||||
seen_nodes[node] = 1 | |||||
# If this file is in a VariantDir and has a | |||||
# corresponding source file in the source tree, remember the | |||||
# node in the source tree, too. This is needed in | |||||
# particular to clear cached implicit dependencies on the | |||||
# source file, since the scanner will scan it if the | |||||
# VariantDir was created with duplicate=0. | |||||
try: | |||||
rfile_method = node.rfile | |||||
except AttributeError: | |||||
return | |||||
else: | |||||
rfile = rfile_method() | |||||
if rfile != node: | |||||
seen_nodes[rfile] = 1 | |||||
for node in nodes: | |||||
walker = SCons.Node.Walker(node, | |||||
kids_func=get_unseen_children, | |||||
eval_func=add_to_seen_nodes) | |||||
n = walker.get_next() | |||||
while n: | |||||
n = walker.get_next() | |||||
for node in list(seen_nodes.keys()): | |||||
# Call node.clear() to clear most of the state | |||||
node.clear() | |||||
# node.clear() doesn't reset node.state, so call | |||||
# node.set_state() to reset it manually | |||||
node.set_state(SCons.Node.no_state) | |||||
node.implicit = None | |||||
# Debug: Uncomment to verify that all Taskmaster reference | |||||
# counts have been reset to zero. | |||||
#if node.ref_count != 0: | |||||
# from SCons.Debug import Trace | |||||
# Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count)) | |||||
SCons.SConsign.Reset() | |||||
SCons.Script.Main.progress_display("scons: done clearing node information.") | |||||
def do_clean(self, argv): | |||||
"""\ | |||||
clean [TARGETS] Clean (remove) the specified TARGETS | |||||
and their dependencies. 'c' is a synonym. | |||||
""" | |||||
return self.do_build(['build', '--clean'] + argv[1:]) | |||||
def do_EOF(self, argv): | |||||
print() | |||||
self.do_exit(argv) | |||||
def _do_one_help(self, arg): | |||||
try: | |||||
# If help_<arg>() exists, then call it. | |||||
func = getattr(self, 'help_' + arg) | |||||
except AttributeError: | |||||
try: | |||||
func = getattr(self, 'do_' + arg) | |||||
except AttributeError: | |||||
doc = None | |||||
else: | |||||
doc = self._doc_to_help(func) | |||||
if doc: | |||||
sys.stdout.write(doc + '\n') | |||||
sys.stdout.flush() | |||||
else: | |||||
doc = self.strip_initial_spaces(func()) | |||||
if doc: | |||||
sys.stdout.write(doc + '\n') | |||||
sys.stdout.flush() | |||||
def _doc_to_help(self, obj): | |||||
doc = obj.__doc__ | |||||
if doc is None: | |||||
return '' | |||||
return self._strip_initial_spaces(doc) | |||||
def _strip_initial_spaces(self, s): | |||||
lines = s.split('\n') | |||||
spaces = re.match(' *', lines[0]).group(0) | |||||
def strip_spaces(l, spaces=spaces): | |||||
if l[:len(spaces)] == spaces: | |||||
l = l[len(spaces):] | |||||
return l | |||||
lines = list(map(strip_spaces, lines)) | |||||
return '\n'.join(lines) | |||||
def do_exit(self, argv): | |||||
"""\ | |||||
exit Exit SCons interactive mode. | |||||
""" | |||||
sys.exit(0) | |||||
def do_help(self, argv): | |||||
"""\ | |||||
help [COMMAND] Prints help for the specified COMMAND. 'h' | |||||
and '?' are synonyms. | |||||
""" | |||||
if argv[1:]: | |||||
for arg in argv[1:]: | |||||
if self._do_one_help(arg): | |||||
break | |||||
else: | |||||
# If bare 'help' is called, print this class's doc | |||||
# string (if it has one). | |||||
doc = self._doc_to_help(self.__class__) | |||||
if doc: | |||||
sys.stdout.write(doc + '\n') | |||||
sys.stdout.flush() | |||||
def do_shell(self, argv): | |||||
"""\ | |||||
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and | |||||
'!' are synonyms. | |||||
""" | |||||
import subprocess | |||||
argv = argv[1:] | |||||
if not argv: | |||||
argv = os.environ[self.shell_variable] | |||||
try: | |||||
# Per "[Python-Dev] subprocess insufficiently platform-independent?" | |||||
# http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+ | |||||
# Doing the right thing with an argument list currently | |||||
# requires different shell= values on Windows and Linux. | |||||
p = subprocess.Popen(argv, shell=(sys.platform=='win32')) | |||||
except EnvironmentError as e: | |||||
sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror)) | |||||
else: | |||||
p.wait() | |||||
def do_version(self, argv): | |||||
"""\ | |||||
version Prints SCons version information. | |||||
""" | |||||
sys.stdout.write(self.parser.version + '\n') | |||||
def interact(fs, parser, options, targets, target_top): | |||||
c = SConsInteractiveCmd(prompt = 'scons>>> ', | |||||
fs = fs, | |||||
parser = parser, | |||||
options = options, | |||||
targets = targets, | |||||
target_top = target_top) | |||||
c.cmdloop() | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,988 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Script/SConsOptions.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import optparse | |||||
import re | |||||
import sys | |||||
import textwrap | |||||
no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') | |||||
try: | |||||
from gettext import gettext | |||||
except ImportError: | |||||
def gettext(message): | |||||
return message | |||||
_ = gettext | |||||
import SCons.Node.FS | |||||
import SCons.Warnings | |||||
OptionValueError = optparse.OptionValueError | |||||
SUPPRESS_HELP = optparse.SUPPRESS_HELP | |||||
diskcheck_all = SCons.Node.FS.diskcheck_types() | |||||
def diskcheck_convert(value): | |||||
if value is None: | |||||
return [] | |||||
if not SCons.Util.is_List(value): | |||||
value = value.split(',') | |||||
result = [] | |||||
for v in value: | |||||
v = v.lower() | |||||
if v == 'all': | |||||
result = diskcheck_all | |||||
elif v == 'none': | |||||
result = [] | |||||
elif v in diskcheck_all: | |||||
result.append(v) | |||||
else: | |||||
raise ValueError(v) | |||||
return result | |||||
class SConsValues(optparse.Values): | |||||
""" | |||||
Holder class for uniform access to SCons options, regardless | |||||
of whether or not they can be set on the command line or in the | |||||
SConscript files (using the SetOption() function). | |||||
A SCons option value can originate three different ways: | |||||
1) set on the command line; | |||||
2) set in an SConscript file; | |||||
3) the default setting (from the the op.add_option() | |||||
calls in the Parser() function, below). | |||||
The command line always overrides a value set in a SConscript file, | |||||
which in turn always overrides default settings. Because we want | |||||
to support user-specified options in the SConscript file itself, | |||||
though, we may not know about all of the options when the command | |||||
line is first parsed, so we can't make all the necessary precedence | |||||
decisions at the time the option is configured. | |||||
The solution implemented in this class is to keep these different sets | |||||
of settings separate (command line, SConscript file, and default) | |||||
and to override the __getattr__() method to check them in turn. | |||||
This should allow the rest of the code to just fetch values as | |||||
attributes of an instance of this class, without having to worry | |||||
about where they came from. | |||||
Note that not all command line options are settable from SConscript | |||||
files, and the ones that are must be explicitly added to the | |||||
"settable" list in this class, and optionally validated and coerced | |||||
in the set_option() method. | |||||
""" | |||||
def __init__(self, defaults): | |||||
self.__dict__['__defaults__'] = defaults | |||||
self.__dict__['__SConscript_settings__'] = {} | |||||
def __getattr__(self, attr): | |||||
""" | |||||
Fetches an options value, checking first for explicit settings | |||||
from the command line (which are direct attributes), then the | |||||
SConscript file settings, then the default values. | |||||
""" | |||||
try: | |||||
return self.__dict__[attr] | |||||
except KeyError: | |||||
try: | |||||
return self.__dict__['__SConscript_settings__'][attr] | |||||
except KeyError: | |||||
try: | |||||
return getattr(self.__dict__['__defaults__'], attr) | |||||
except KeyError: | |||||
# Added because with py3 this is a new class, | |||||
# not a classic class, and due to the way | |||||
# In that case it will create an object without | |||||
# __defaults__, and then query for __setstate__ | |||||
# which will throw an exception of KeyError | |||||
# deepcopy() is expecting AttributeError if __setstate__ | |||||
# is not available. | |||||
raise AttributeError(attr) | |||||
settable = [ | |||||
'clean', | |||||
'diskcheck', | |||||
'duplicate', | |||||
'help', | |||||
'implicit_cache', | |||||
'max_drift', | |||||
'md5_chunksize', | |||||
'no_exec', | |||||
'num_jobs', | |||||
'random', | |||||
'stack_size', | |||||
'warn', | |||||
'silent' | |||||
] | |||||
def set_option(self, name, value): | |||||
""" | |||||
Sets an option from an SConscript file. | |||||
""" | |||||
if not name in self.settable: | |||||
raise SCons.Errors.UserError("This option is not settable from a SConscript file: %s"%name) | |||||
if name == 'num_jobs': | |||||
try: | |||||
value = int(value) | |||||
if value < 1: | |||||
raise ValueError | |||||
except ValueError: | |||||
raise SCons.Errors.UserError("A positive integer is required: %s"%repr(value)) | |||||
elif name == 'max_drift': | |||||
try: | |||||
value = int(value) | |||||
except ValueError: | |||||
raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) | |||||
elif name == 'duplicate': | |||||
try: | |||||
value = str(value) | |||||
except ValueError: | |||||
raise SCons.Errors.UserError("A string is required: %s"%repr(value)) | |||||
if not value in SCons.Node.FS.Valid_Duplicates: | |||||
raise SCons.Errors.UserError("Not a valid duplication style: %s" % value) | |||||
# Set the duplicate style right away so it can affect linking | |||||
# of SConscript files. | |||||
SCons.Node.FS.set_duplicate(value) | |||||
elif name == 'diskcheck': | |||||
try: | |||||
value = diskcheck_convert(value) | |||||
except ValueError as v: | |||||
raise SCons.Errors.UserError("Not a valid diskcheck value: %s"%v) | |||||
if 'diskcheck' not in self.__dict__: | |||||
# No --diskcheck= option was specified on the command line. | |||||
# Set this right away so it can affect the rest of the | |||||
# file/Node lookups while processing the SConscript files. | |||||
SCons.Node.FS.set_diskcheck(value) | |||||
elif name == 'stack_size': | |||||
try: | |||||
value = int(value) | |||||
except ValueError: | |||||
raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) | |||||
elif name == 'md5_chunksize': | |||||
try: | |||||
value = int(value) | |||||
except ValueError: | |||||
raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) | |||||
elif name == 'warn': | |||||
if SCons.Util.is_String(value): | |||||
value = [value] | |||||
value = self.__SConscript_settings__.get(name, []) + value | |||||
SCons.Warnings.process_warn_strings(value) | |||||
self.__SConscript_settings__[name] = value | |||||
class SConsOption(optparse.Option): | |||||
def convert_value(self, opt, value): | |||||
if value is not None: | |||||
if self.nargs in (1, '?'): | |||||
return self.check_value(opt, value) | |||||
else: | |||||
return tuple([self.check_value(opt, v) for v in value]) | |||||
def process(self, opt, value, values, parser): | |||||
# First, convert the value(s) to the right type. Howl if any | |||||
# value(s) are bogus. | |||||
value = self.convert_value(opt, value) | |||||
# And then take whatever action is expected of us. | |||||
# This is a separate method to make life easier for | |||||
# subclasses to add new actions. | |||||
return self.take_action( | |||||
self.action, self.dest, opt, value, values, parser) | |||||
def _check_nargs_optional(self): | |||||
if self.nargs == '?' and self._short_opts: | |||||
fmt = "option %s: nargs='?' is incompatible with short options" | |||||
raise SCons.Errors.UserError(fmt % self._short_opts[0]) | |||||
try: | |||||
_orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS | |||||
_orig_CHECK_METHODS = optparse.Option.CHECK_METHODS | |||||
except AttributeError: | |||||
# optparse.Option had no CONST_ACTIONS before Python 2.5. | |||||
_orig_CONST_ACTIONS = ("store_const",) | |||||
def _check_const(self): | |||||
if self.action not in self.CONST_ACTIONS and self.const is not None: | |||||
raise OptionError( | |||||
"'const' must not be supplied for action %r" % self.action, | |||||
self) | |||||
# optparse.Option collects its list of unbound check functions | |||||
# up front. This sucks because it means we can't just override | |||||
# the _check_const() function like a normal method, we have to | |||||
# actually replace it in the list. This seems to be the most | |||||
# straightforward way to do that. | |||||
_orig_CHECK_METHODS = [optparse.Option._check_action, | |||||
optparse.Option._check_type, | |||||
optparse.Option._check_choice, | |||||
optparse.Option._check_dest, | |||||
_check_const, | |||||
optparse.Option._check_nargs, | |||||
optparse.Option._check_callback] | |||||
CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] | |||||
CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS | |||||
class SConsOptionGroup(optparse.OptionGroup): | |||||
""" | |||||
A subclass for SCons-specific option groups. | |||||
The only difference between this and the base class is that we print | |||||
the group's help text flush left, underneath their own title but | |||||
lined up with the normal "SCons Options". | |||||
""" | |||||
def format_help(self, formatter): | |||||
""" | |||||
Format an option group's help text, outdenting the title so it's | |||||
flush with the "SCons Options" title we print at the top. | |||||
""" | |||||
formatter.dedent() | |||||
result = formatter.format_heading(self.title) | |||||
formatter.indent() | |||||
result = result + optparse.OptionContainer.format_help(self, formatter) | |||||
return result | |||||
class SConsOptionParser(optparse.OptionParser): | |||||
preserve_unknown_options = False | |||||
def error(self, msg): | |||||
# overridden OptionValueError exception handler | |||||
self.print_usage(sys.stderr) | |||||
sys.stderr.write("SCons Error: %s\n" % msg) | |||||
sys.exit(2) | |||||
def _process_long_opt(self, rargs, values): | |||||
""" | |||||
SCons-specific processing of long options. | |||||
This is copied directly from the normal | |||||
optparse._process_long_opt() method, except that, if configured | |||||
to do so, we catch the exception thrown when an unknown option | |||||
is encountered and just stick it back on the "leftover" arguments | |||||
for later (re-)processing. | |||||
""" | |||||
arg = rargs.pop(0) | |||||
# Value explicitly attached to arg? Pretend it's the next | |||||
# argument. | |||||
if "=" in arg: | |||||
(opt, next_arg) = arg.split("=", 1) | |||||
rargs.insert(0, next_arg) | |||||
had_explicit_value = True | |||||
else: | |||||
opt = arg | |||||
had_explicit_value = False | |||||
try: | |||||
opt = self._match_long_opt(opt) | |||||
except optparse.BadOptionError: | |||||
if self.preserve_unknown_options: | |||||
# SCons-specific: if requested, add unknown options to | |||||
# the "leftover arguments" list for later processing. | |||||
self.largs.append(arg) | |||||
if had_explicit_value: | |||||
# The unknown option will be re-processed later, | |||||
# so undo the insertion of the explicit value. | |||||
rargs.pop(0) | |||||
return | |||||
raise | |||||
option = self._long_opt[opt] | |||||
if option.takes_value(): | |||||
nargs = option.nargs | |||||
if nargs == '?': | |||||
if had_explicit_value: | |||||
value = rargs.pop(0) | |||||
else: | |||||
value = option.const | |||||
elif len(rargs) < nargs: | |||||
if nargs == 1: | |||||
if not option.choices: | |||||
self.error(_("%s option requires an argument") % opt) | |||||
else: | |||||
msg = _("%s option requires an argument " % opt) | |||||
msg += _("(choose from %s)" | |||||
% ', '.join(option.choices)) | |||||
self.error(msg) | |||||
else: | |||||
self.error(_("%s option requires %d arguments") | |||||
% (opt, nargs)) | |||||
elif nargs == 1: | |||||
value = rargs.pop(0) | |||||
else: | |||||
value = tuple(rargs[0:nargs]) | |||||
del rargs[0:nargs] | |||||
elif had_explicit_value: | |||||
self.error(_("%s option does not take a value") % opt) | |||||
else: | |||||
value = None | |||||
option.process(opt, value, values, self) | |||||
def reparse_local_options(self): | |||||
""" | |||||
Re-parse the leftover command-line options stored | |||||
in self.largs, so that any value overridden on the | |||||
command line is immediately available if the user turns | |||||
around and does a GetOption() right away. | |||||
We mimic the processing of the single args | |||||
in the original OptionParser._process_args(), but here we | |||||
allow exact matches for long-opts only (no partial | |||||
argument names!). | |||||
Else, this would lead to problems in add_local_option() | |||||
below. When called from there, we try to reparse the | |||||
command-line arguments that | |||||
1. haven't been processed so far (self.largs), but | |||||
2. are possibly not added to the list of options yet. | |||||
So, when we only have a value for "--myargument" yet, | |||||
a command-line argument of "--myarg=test" would set it. | |||||
Responsible for this behaviour is the method | |||||
_match_long_opt(), which allows for partial matches of | |||||
the option name, as long as the common prefix appears to | |||||
be unique. | |||||
This would lead to further confusion, because we might want | |||||
to add another option "--myarg" later on (see issue #2929). | |||||
""" | |||||
rargs = [] | |||||
largs_restore = [] | |||||
# Loop over all remaining arguments | |||||
skip = False | |||||
for l in self.largs: | |||||
if skip: | |||||
# Accept all remaining arguments as they are | |||||
largs_restore.append(l) | |||||
else: | |||||
if len(l) > 2 and l[0:2] == "--": | |||||
# Check long option | |||||
lopt = (l,) | |||||
if "=" in l: | |||||
# Split into option and value | |||||
lopt = l.split("=", 1) | |||||
if lopt[0] in self._long_opt: | |||||
# Argument is already known | |||||
rargs.append('='.join(lopt)) | |||||
else: | |||||
# Not known yet, so reject for now | |||||
largs_restore.append('='.join(lopt)) | |||||
else: | |||||
if l == "--" or l == "-": | |||||
# Stop normal processing and don't | |||||
# process the rest of the command-line opts | |||||
largs_restore.append(l) | |||||
skip = True | |||||
else: | |||||
rargs.append(l) | |||||
# Parse the filtered list | |||||
self.parse_args(rargs, self.values) | |||||
# Restore the list of remaining arguments for the | |||||
# next call of AddOption/add_local_option... | |||||
self.largs = self.largs + largs_restore | |||||
def add_local_option(self, *args, **kw): | |||||
""" | |||||
Adds a local option to the parser. | |||||
This is initiated by a SetOption() call to add a user-defined | |||||
command-line option. We add the option to a separate option | |||||
group for the local options, creating the group if necessary. | |||||
""" | |||||
try: | |||||
group = self.local_option_group | |||||
except AttributeError: | |||||
group = SConsOptionGroup(self, 'Local Options') | |||||
group = self.add_option_group(group) | |||||
self.local_option_group = group | |||||
result = group.add_option(*args, **kw) | |||||
if result: | |||||
# The option was added successfully. We now have to add the | |||||
# default value to our object that holds the default values | |||||
# (so that an attempt to fetch the option's attribute will | |||||
# yield the default value when not overridden) and then | |||||
# we re-parse the leftover command-line options, so that | |||||
# any value overridden on the command line is immediately | |||||
# available if the user turns around and does a GetOption() | |||||
# right away. | |||||
setattr(self.values.__defaults__, result.dest, result.default) | |||||
self.reparse_local_options() | |||||
return result | |||||
class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): | |||||
def format_usage(self, usage): | |||||
return "usage: %s\n" % usage | |||||
def format_heading(self, heading): | |||||
""" | |||||
This translates any heading of "options" or "Options" into | |||||
"SCons Options." Unfortunately, we have to do this here, | |||||
because those titles are hard-coded in the optparse calls. | |||||
""" | |||||
if heading == 'Options': | |||||
heading = "SCons Options" | |||||
return optparse.IndentedHelpFormatter.format_heading(self, heading) | |||||
def format_option(self, option): | |||||
""" | |||||
A copy of the normal optparse.IndentedHelpFormatter.format_option() | |||||
method. This has been snarfed so we can modify text wrapping to | |||||
out liking: | |||||
-- add our own regular expression that doesn't break on hyphens | |||||
(so things like --no-print-directory don't get broken); | |||||
-- wrap the list of options themselves when it's too long | |||||
(the wrapper.fill(opts) call below); | |||||
-- set the subsequent_indent when wrapping the help_text. | |||||
""" | |||||
# The help for each option consists of two parts: | |||||
# * the opt strings and metavars | |||||
# eg. ("-x", or "-fFILENAME, --file=FILENAME") | |||||
# * the user-supplied help string | |||||
# eg. ("turn on expert mode", "read data from FILENAME") | |||||
# | |||||
# If possible, we write both of these on the same line: | |||||
# -x turn on expert mode | |||||
# | |||||
# But if the opt string list is too long, we put the help | |||||
# string on a second line, indented to the same column it would | |||||
# start in if it fit on the first line. | |||||
# -fFILENAME, --file=FILENAME | |||||
# read data from FILENAME | |||||
result = [] | |||||
opts = self.option_strings[option] | |||||
opt_width = self.help_position - self.current_indent - 2 | |||||
if len(opts) > opt_width: | |||||
wrapper = textwrap.TextWrapper(width=self.width, | |||||
initial_indent = ' ', | |||||
subsequent_indent = ' ') | |||||
wrapper.wordsep_re = no_hyphen_re | |||||
opts = wrapper.fill(opts) + '\n' | |||||
indent_first = self.help_position | |||||
else: # start help on same line as opts | |||||
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) | |||||
indent_first = 0 | |||||
result.append(opts) | |||||
if option.help: | |||||
help_text = self.expand_default(option) | |||||
# SCons: indent every line of the help text but the first. | |||||
wrapper = textwrap.TextWrapper(width=self.help_width, | |||||
subsequent_indent = ' ') | |||||
wrapper.wordsep_re = no_hyphen_re | |||||
help_lines = wrapper.wrap(help_text) | |||||
result.append("%*s%s\n" % (indent_first, "", help_lines[0])) | |||||
for line in help_lines[1:]: | |||||
result.append("%*s%s\n" % (self.help_position, "", line)) | |||||
elif opts[-1] != "\n": | |||||
result.append("\n") | |||||
return "".join(result) | |||||
def Parser(version): | |||||
""" | |||||
Returns an options parser object initialized with the standard | |||||
SCons options. | |||||
""" | |||||
formatter = SConsIndentedHelpFormatter(max_help_position=30) | |||||
op = SConsOptionParser(option_class=SConsOption, | |||||
add_help_option=False, | |||||
formatter=formatter, | |||||
usage="usage: scons [OPTION] [TARGET] ...",) | |||||
op.preserve_unknown_options = True | |||||
op.version = version | |||||
# Add the options to the parser we just created. | |||||
# | |||||
# These are in the order we want them to show up in the -H help | |||||
# text, basically alphabetical. Each op.add_option() call below | |||||
# should have a consistent format: | |||||
# | |||||
# op.add_option("-L", "--long-option-name", | |||||
# nargs=1, type="string", | |||||
# dest="long_option_name", default='foo', | |||||
# action="callback", callback=opt_long_option, | |||||
# help="help text goes here", | |||||
# metavar="VAR") | |||||
# | |||||
# Even though the optparse module constructs reasonable default | |||||
# destination names from the long option names, we're going to be | |||||
# explicit about each one for easier readability and so this code | |||||
# will at least show up when grepping the source for option attribute | |||||
# names, or otherwise browsing the source code. | |||||
# options ignored for compatibility | |||||
def opt_ignore(option, opt, value, parser): | |||||
sys.stderr.write("Warning: ignoring %s option\n" % opt) | |||||
op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w", | |||||
"--environment-overrides", | |||||
"--no-keep-going", | |||||
"--no-print-directory", | |||||
"--print-directory", | |||||
"--stop", | |||||
"--touch", | |||||
action="callback", callback=opt_ignore, | |||||
help="Ignored for compatibility.") | |||||
op.add_option('-c', '--clean', '--remove', | |||||
dest="clean", default=False, | |||||
action="store_true", | |||||
help="Remove specified targets and dependencies.") | |||||
op.add_option('-C', '--directory', | |||||
nargs=1, type="string", | |||||
dest="directory", default=[], | |||||
action="append", | |||||
help="Change to DIR before doing anything.", | |||||
metavar="DIR") | |||||
op.add_option('--cache-debug', | |||||
nargs=1, | |||||
dest="cache_debug", default=None, | |||||
action="store", | |||||
help="Print CacheDir debug info to FILE.", | |||||
metavar="FILE") | |||||
op.add_option('--cache-disable', '--no-cache', | |||||
dest='cache_disable', default=False, | |||||
action="store_true", | |||||
help="Do not retrieve built targets from CacheDir.") | |||||
op.add_option('--cache-force', '--cache-populate', | |||||
dest='cache_force', default=False, | |||||
action="store_true", | |||||
help="Copy already-built targets into the CacheDir.") | |||||
op.add_option('--cache-readonly', | |||||
dest='cache_readonly', default=False, | |||||
action="store_true", | |||||
help="Do not update CacheDir with built targets.") | |||||
op.add_option('--cache-show', | |||||
dest='cache_show', default=False, | |||||
action="store_true", | |||||
help="Print build actions for files from CacheDir.") | |||||
def opt_invalid(group, value, options): | |||||
errmsg = "`%s' is not a valid %s option type, try:\n" % (value, group) | |||||
return errmsg + " %s" % ", ".join(options) | |||||
config_options = ["auto", "force" ,"cache"] | |||||
opt_config_help = "Controls Configure subsystem: %s." \ | |||||
% ", ".join(config_options) | |||||
op.add_option('--config', | |||||
nargs=1, choices=config_options, | |||||
dest="config", default="auto", | |||||
help = opt_config_help, | |||||
metavar="MODE") | |||||
op.add_option('-D', | |||||
dest="climb_up", default=None, | |||||
action="store_const", const=2, | |||||
help="Search up directory tree for SConstruct, " | |||||
"build all Default() targets.") | |||||
deprecated_debug_options = { | |||||
"dtree" : '; please use --tree=derived instead', | |||||
"nomemoizer" : ' and has no effect', | |||||
"stree" : '; please use --tree=all,status instead', | |||||
"tree" : '; please use --tree=all instead', | |||||
} | |||||
debug_options = ["count", "duplicate", "explain", "findlibs", | |||||
"includes", "memoizer", "memory", "objects", | |||||
"pdb", "prepare", "presub", "stacktrace", | |||||
"time"] | |||||
def opt_debug(option, opt, value__, parser, | |||||
debug_options=debug_options, | |||||
deprecated_debug_options=deprecated_debug_options): | |||||
for value in value__.split(','): | |||||
if value in debug_options: | |||||
parser.values.debug.append(value) | |||||
elif value in list(deprecated_debug_options.keys()): | |||||
parser.values.debug.append(value) | |||||
try: | |||||
parser.values.delayed_warnings | |||||
except AttributeError: | |||||
parser.values.delayed_warnings = [] | |||||
msg = deprecated_debug_options[value] | |||||
w = "The --debug=%s option is deprecated%s." % (value, msg) | |||||
t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w) | |||||
parser.values.delayed_warnings.append(t) | |||||
else: | |||||
raise OptionValueError(opt_invalid('debug', value, debug_options)) | |||||
opt_debug_help = "Print various types of debugging information: %s." \ | |||||
% ", ".join(debug_options) | |||||
op.add_option('--debug', | |||||
nargs=1, type="string", | |||||
dest="debug", default=[], | |||||
action="callback", callback=opt_debug, | |||||
help=opt_debug_help, | |||||
metavar="TYPE") | |||||
def opt_diskcheck(option, opt, value, parser): | |||||
try: | |||||
diskcheck_value = diskcheck_convert(value) | |||||
except ValueError as e: | |||||
raise OptionValueError("`%s' is not a valid diskcheck type" % e) | |||||
setattr(parser.values, option.dest, diskcheck_value) | |||||
op.add_option('--diskcheck', | |||||
nargs=1, type="string", | |||||
dest='diskcheck', default=None, | |||||
action="callback", callback=opt_diskcheck, | |||||
help="Enable specific on-disk checks.", | |||||
metavar="TYPE") | |||||
def opt_duplicate(option, opt, value, parser): | |||||
if not value in SCons.Node.FS.Valid_Duplicates: | |||||
raise OptionValueError(opt_invalid('duplication', value, | |||||
SCons.Node.FS.Valid_Duplicates)) | |||||
setattr(parser.values, option.dest, value) | |||||
# Set the duplicate style right away so it can affect linking | |||||
# of SConscript files. | |||||
SCons.Node.FS.set_duplicate(value) | |||||
opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \ | |||||
+ ", ".join(SCons.Node.FS.Valid_Duplicates) | |||||
op.add_option('--duplicate', | |||||
nargs=1, type="string", | |||||
dest="duplicate", default='hard-soft-copy', | |||||
action="callback", callback=opt_duplicate, | |||||
help=opt_duplicate_help) | |||||
op.add_option('-f', '--file', '--makefile', '--sconstruct', | |||||
nargs=1, type="string", | |||||
dest="file", default=[], | |||||
action="append", | |||||
help="Read FILE as the top-level SConstruct file.") | |||||
op.add_option('-h', '--help', | |||||
dest="help", default=False, | |||||
action="store_true", | |||||
help="Print defined help message, or this one.") | |||||
op.add_option("-H", "--help-options", | |||||
action="help", | |||||
help="Print this message and exit.") | |||||
op.add_option('-i', '--ignore-errors', | |||||
dest='ignore_errors', default=False, | |||||
action="store_true", | |||||
help="Ignore errors from build actions.") | |||||
op.add_option('-I', '--include-dir', | |||||
nargs=1, | |||||
dest='include_dir', default=[], | |||||
action="append", | |||||
help="Search DIR for imported Python modules.", | |||||
metavar="DIR") | |||||
op.add_option('--implicit-cache', | |||||
dest='implicit_cache', default=False, | |||||
action="store_true", | |||||
help="Cache implicit dependencies") | |||||
def opt_implicit_deps(option, opt, value, parser): | |||||
setattr(parser.values, 'implicit_cache', True) | |||||
setattr(parser.values, option.dest, True) | |||||
op.add_option('--implicit-deps-changed', | |||||
dest="implicit_deps_changed", default=False, | |||||
action="callback", callback=opt_implicit_deps, | |||||
help="Ignore cached implicit dependencies.") | |||||
op.add_option('--implicit-deps-unchanged', | |||||
dest="implicit_deps_unchanged", default=False, | |||||
action="callback", callback=opt_implicit_deps, | |||||
help="Ignore changes in implicit dependencies.") | |||||
op.add_option('--interact', '--interactive', | |||||
dest='interactive', default=False, | |||||
action="store_true", | |||||
help="Run in interactive mode.") | |||||
op.add_option('-j', '--jobs', | |||||
nargs=1, type="int", | |||||
dest="num_jobs", default=1, | |||||
action="store", | |||||
help="Allow N jobs at once.", | |||||
metavar="N") | |||||
op.add_option('-k', '--keep-going', | |||||
dest='keep_going', default=False, | |||||
action="store_true", | |||||
help="Keep going when a target can't be made.") | |||||
op.add_option('--max-drift', | |||||
nargs=1, type="int", | |||||
dest='max_drift', default=SCons.Node.FS.default_max_drift, | |||||
action="store", | |||||
help="Set maximum system clock drift to N seconds.", | |||||
metavar="N") | |||||
op.add_option('--md5-chunksize', | |||||
nargs=1, type="int", | |||||
dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize, | |||||
action="store", | |||||
help="Set chunk-size for MD5 signature computation to N kilobytes.", | |||||
metavar="N") | |||||
op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', | |||||
dest='no_exec', default=False, | |||||
action="store_true", | |||||
help="Don't build; just print commands.") | |||||
op.add_option('--no-site-dir', | |||||
dest='no_site_dir', default=False, | |||||
action="store_true", | |||||
help="Don't search or use the usual site_scons dir.") | |||||
op.add_option('--profile', | |||||
nargs=1, | |||||
dest="profile_file", default=None, | |||||
action="store", | |||||
help="Profile SCons and put results in FILE.", | |||||
metavar="FILE") | |||||
op.add_option('-q', '--question', | |||||
dest="question", default=False, | |||||
action="store_true", | |||||
help="Don't build; exit status says if up to date.") | |||||
op.add_option('-Q', | |||||
dest='no_progress', default=False, | |||||
action="store_true", | |||||
help="Suppress \"Reading/Building\" progress messages.") | |||||
op.add_option('--random', | |||||
dest="random", default=False, | |||||
action="store_true", | |||||
help="Build dependencies in random order.") | |||||
op.add_option('-s', '--silent', '--quiet', | |||||
dest="silent", default=False, | |||||
action="store_true", | |||||
help="Don't print commands.") | |||||
op.add_option('--site-dir', | |||||
nargs=1, | |||||
dest='site_dir', default=None, | |||||
action="store", | |||||
help="Use DIR instead of the usual site_scons dir.", | |||||
metavar="DIR") | |||||
op.add_option('--stack-size', | |||||
nargs=1, type="int", | |||||
dest='stack_size', | |||||
action="store", | |||||
help="Set the stack size of the threads used to run jobs to N kilobytes.", | |||||
metavar="N") | |||||
op.add_option('--taskmastertrace', | |||||
nargs=1, | |||||
dest="taskmastertrace_file", default=None, | |||||
action="store", | |||||
help="Trace Node evaluation to FILE.", | |||||
metavar="FILE") | |||||
tree_options = ["all", "derived", "prune", "status"] | |||||
def opt_tree(option, opt, value, parser, tree_options=tree_options): | |||||
from . import Main | |||||
tp = Main.TreePrinter() | |||||
for o in value.split(','): | |||||
if o == 'all': | |||||
tp.derived = False | |||||
elif o == 'derived': | |||||
tp.derived = True | |||||
elif o == 'prune': | |||||
tp.prune = True | |||||
elif o == 'status': | |||||
tp.status = True | |||||
else: | |||||
raise OptionValueError(opt_invalid('--tree', o, tree_options)) | |||||
parser.values.tree_printers.append(tp) | |||||
opt_tree_help = "Print a dependency tree in various formats: %s." \ | |||||
% ", ".join(tree_options) | |||||
op.add_option('--tree', | |||||
nargs=1, type="string", | |||||
dest="tree_printers", default=[], | |||||
action="callback", callback=opt_tree, | |||||
help=opt_tree_help, | |||||
metavar="OPTIONS") | |||||
op.add_option('-u', '--up', '--search-up', | |||||
dest="climb_up", default=0, | |||||
action="store_const", const=1, | |||||
help="Search up directory tree for SConstruct, " | |||||
"build targets at or below current directory.") | |||||
op.add_option('-U', | |||||
dest="climb_up", default=0, | |||||
action="store_const", const=3, | |||||
help="Search up directory tree for SConstruct, " | |||||
"build Default() targets from local SConscript.") | |||||
def opt_version(option, opt, value, parser): | |||||
sys.stdout.write(parser.version + '\n') | |||||
sys.exit(0) | |||||
op.add_option("-v", "--version", | |||||
action="callback", callback=opt_version, | |||||
help="Print the SCons version number and exit.") | |||||
def opt_warn(option, opt, value, parser, tree_options=tree_options): | |||||
if SCons.Util.is_String(value): | |||||
value = value.split(',') | |||||
parser.values.warn.extend(value) | |||||
op.add_option('--warn', '--warning', | |||||
nargs=1, type="string", | |||||
dest="warn", default=[], | |||||
action="callback", callback=opt_warn, | |||||
help="Enable or disable warnings.", | |||||
metavar="WARNING-SPEC") | |||||
op.add_option('-Y', '--repository', '--srcdir', | |||||
nargs=1, | |||||
dest="repository", default=[], | |||||
action="append", | |||||
help="Search REPOSITORY for source and target files.") | |||||
# Options from Make and Cons classic that we do not yet support, | |||||
# but which we may support someday and whose (potential) meanings | |||||
# we don't want to change. These all get a "the -X option is not | |||||
# yet implemented" message and don't show up in the help output. | |||||
def opt_not_yet(option, opt, value, parser): | |||||
msg = "Warning: the %s option is not yet implemented\n" % opt | |||||
sys.stderr.write(msg) | |||||
op.add_option('-l', '--load-average', '--max-load', | |||||
nargs=1, type="float", | |||||
dest="load_average", default=0, | |||||
action="callback", callback=opt_not_yet, | |||||
# action="store", | |||||
# help="Don't start multiple jobs unless load is below " | |||||
# "LOAD-AVERAGE." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('--list-actions', | |||||
dest="list_actions", | |||||
action="callback", callback=opt_not_yet, | |||||
# help="Don't build; list files and build actions." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('--list-derived', | |||||
dest="list_derived", | |||||
action="callback", callback=opt_not_yet, | |||||
# help="Don't build; list files that would be built." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('--list-where', | |||||
dest="list_where", | |||||
action="callback", callback=opt_not_yet, | |||||
# help="Don't build; list files and where defined." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('-o', '--old-file', '--assume-old', | |||||
nargs=1, type="string", | |||||
dest="old_file", default=[], | |||||
action="callback", callback=opt_not_yet, | |||||
# action="append", | |||||
# help = "Consider FILE to be old; don't rebuild it." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('--override', | |||||
nargs=1, type="string", | |||||
action="callback", callback=opt_not_yet, | |||||
dest="override", | |||||
# help="Override variables as specified in FILE." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('-p', | |||||
action="callback", callback=opt_not_yet, | |||||
dest="p", | |||||
# help="Print internal environments/objects." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables', | |||||
action="callback", callback=opt_not_yet, | |||||
dest="no_builtin_rules", | |||||
# help="Clear default environments and variables." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('--write-filenames', | |||||
nargs=1, type="string", | |||||
dest="write_filenames", | |||||
action="callback", callback=opt_not_yet, | |||||
# help="Write all filenames examined into FILE." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('-W', '--new-file', '--assume-new', '--what-if', | |||||
nargs=1, type="string", | |||||
dest="new_file", | |||||
action="callback", callback=opt_not_yet, | |||||
# help="Consider FILE to be changed." | |||||
help=SUPPRESS_HELP) | |||||
op.add_option('--warn-undefined-variables', | |||||
dest="warn_undefined_variables", | |||||
action="callback", callback=opt_not_yet, | |||||
# help="Warn when an undefined variable is referenced." | |||||
help=SUPPRESS_HELP) | |||||
return op | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,633 @@ | |||||
"""SCons.Script.SConscript | |||||
This module defines the Python API provided to SConscript and SConstruct | |||||
files. | |||||
""" | |||||
from __future__ import print_function | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Script/SConscript.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons | |||||
import SCons.Action | |||||
import SCons.Builder | |||||
import SCons.Defaults | |||||
import SCons.Environment | |||||
import SCons.Errors | |||||
import SCons.Node | |||||
import SCons.Node.Alias | |||||
import SCons.Node.FS | |||||
import SCons.Platform | |||||
import SCons.SConf | |||||
import SCons.Script.Main | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
import collections | |||||
import os | |||||
import os.path | |||||
import re | |||||
import sys | |||||
import traceback | |||||
class SConscriptReturn(Exception): | |||||
pass | |||||
launch_dir = os.path.abspath(os.curdir) | |||||
GlobalDict = None | |||||
# global exports set by Export(): | |||||
global_exports = {} | |||||
# chdir flag | |||||
sconscript_chdir = 1 | |||||
def get_calling_namespaces(): | |||||
"""Return the locals and globals for the function that called | |||||
into this module in the current call stack.""" | |||||
try: 1//0 | |||||
except ZeroDivisionError: | |||||
# Don't start iterating with the current stack-frame to | |||||
# prevent creating reference cycles (f_back is safe). | |||||
frame = sys.exc_info()[2].tb_frame.f_back | |||||
# Find the first frame that *isn't* from this file. This means | |||||
# that we expect all of the SCons frames that implement an Export() | |||||
# or SConscript() call to be in this file, so that we can identify | |||||
# the first non-Script.SConscript frame as the user's local calling | |||||
# environment, and the locals and globals dictionaries from that | |||||
# frame as the calling namespaces. See the comment below preceding | |||||
# the DefaultEnvironmentCall block for even more explanation. | |||||
while frame.f_globals.get("__name__") == __name__: | |||||
frame = frame.f_back | |||||
return frame.f_locals, frame.f_globals | |||||
def compute_exports(exports): | |||||
"""Compute a dictionary of exports given one of the parameters | |||||
to the Export() function or the exports argument to SConscript().""" | |||||
loc, glob = get_calling_namespaces() | |||||
retval = {} | |||||
try: | |||||
for export in exports: | |||||
if SCons.Util.is_Dict(export): | |||||
retval.update(export) | |||||
else: | |||||
try: | |||||
retval[export] = loc[export] | |||||
except KeyError: | |||||
retval[export] = glob[export] | |||||
except KeyError as x: | |||||
raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x) | |||||
return retval | |||||
class Frame(object): | |||||
"""A frame on the SConstruct/SConscript call stack""" | |||||
def __init__(self, fs, exports, sconscript): | |||||
self.globals = BuildDefaultGlobals() | |||||
self.retval = None | |||||
self.prev_dir = fs.getcwd() | |||||
self.exports = compute_exports(exports) # exports from the calling SConscript | |||||
# make sure the sconscript attr is a Node. | |||||
if isinstance(sconscript, SCons.Node.Node): | |||||
self.sconscript = sconscript | |||||
elif sconscript == '-': | |||||
self.sconscript = None | |||||
else: | |||||
self.sconscript = fs.File(str(sconscript)) | |||||
# the SConstruct/SConscript call stack: | |||||
call_stack = [] | |||||
# For documentation on the methods in this file, see the scons man-page | |||||
def Return(*vars, **kw): | |||||
retval = [] | |||||
try: | |||||
fvars = SCons.Util.flatten(vars) | |||||
for var in fvars: | |||||
for v in var.split(): | |||||
retval.append(call_stack[-1].globals[v]) | |||||
except KeyError as x: | |||||
raise SCons.Errors.UserError("Return of non-existent variable '%s'"%x) | |||||
if len(retval) == 1: | |||||
call_stack[-1].retval = retval[0] | |||||
else: | |||||
call_stack[-1].retval = tuple(retval) | |||||
stop = kw.get('stop', True) | |||||
if stop: | |||||
raise SConscriptReturn | |||||
stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) | |||||
def _SConscript(fs, *files, **kw): | |||||
top = fs.Top | |||||
sd = fs.SConstruct_dir.rdir() | |||||
exports = kw.get('exports', []) | |||||
# evaluate each SConscript file | |||||
results = [] | |||||
for fn in files: | |||||
call_stack.append(Frame(fs, exports, fn)) | |||||
old_sys_path = sys.path | |||||
try: | |||||
SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1 | |||||
if fn == "-": | |||||
exec(sys.stdin.read(), call_stack[-1].globals) | |||||
else: | |||||
if isinstance(fn, SCons.Node.Node): | |||||
f = fn | |||||
else: | |||||
f = fs.File(str(fn)) | |||||
_file_ = None | |||||
# Change directory to the top of the source | |||||
# tree to make sure the os's cwd and the cwd of | |||||
# fs match so we can open the SConscript. | |||||
fs.chdir(top, change_os_dir=1) | |||||
if f.rexists(): | |||||
actual = f.rfile() | |||||
_file_ = open(actual.get_abspath(), "rb") | |||||
elif f.srcnode().rexists(): | |||||
actual = f.srcnode().rfile() | |||||
_file_ = open(actual.get_abspath(), "rb") | |||||
elif f.has_src_builder(): | |||||
# The SConscript file apparently exists in a source | |||||
# code management system. Build it, but then clear | |||||
# the builder so that it doesn't get built *again* | |||||
# during the actual build phase. | |||||
f.build() | |||||
f.built() | |||||
f.builder_set(None) | |||||
if f.exists(): | |||||
_file_ = open(f.get_abspath(), "rb") | |||||
if _file_: | |||||
# Chdir to the SConscript directory. Use a path | |||||
# name relative to the SConstruct file so that if | |||||
# we're using the -f option, we're essentially | |||||
# creating a parallel SConscript directory structure | |||||
# in our local directory tree. | |||||
# | |||||
# XXX This is broken for multiple-repository cases | |||||
# where the SConstruct and SConscript files might be | |||||
# in different Repositories. For now, cross that | |||||
# bridge when someone comes to it. | |||||
try: | |||||
src_dir = kw['src_dir'] | |||||
except KeyError: | |||||
ldir = fs.Dir(f.dir.get_path(sd)) | |||||
else: | |||||
ldir = fs.Dir(src_dir) | |||||
if not ldir.is_under(f.dir): | |||||
# They specified a source directory, but | |||||
# it's above the SConscript directory. | |||||
# Do the sensible thing and just use the | |||||
# SConcript directory. | |||||
ldir = fs.Dir(f.dir.get_path(sd)) | |||||
try: | |||||
fs.chdir(ldir, change_os_dir=sconscript_chdir) | |||||
except OSError: | |||||
# There was no local directory, so we should be | |||||
# able to chdir to the Repository directory. | |||||
# Note that we do this directly, not through | |||||
# fs.chdir(), because we still need to | |||||
# interpret the stuff within the SConscript file | |||||
# relative to where we are logically. | |||||
fs.chdir(ldir, change_os_dir=0) | |||||
os.chdir(actual.dir.get_abspath()) | |||||
# Append the SConscript directory to the beginning | |||||
# of sys.path so Python modules in the SConscript | |||||
# directory can be easily imported. | |||||
sys.path = [ f.dir.get_abspath() ] + sys.path | |||||
# This is the magic line that actually reads up | |||||
# and executes the stuff in the SConscript file. | |||||
# The locals for this frame contain the special | |||||
# bottom-of-the-stack marker so that any | |||||
# exceptions that occur when processing this | |||||
# SConscript can base the printed frames at this | |||||
# level and not show SCons internals as well. | |||||
call_stack[-1].globals.update({stack_bottom:1}) | |||||
old_file = call_stack[-1].globals.get('__file__') | |||||
try: | |||||
del call_stack[-1].globals['__file__'] | |||||
except KeyError: | |||||
pass | |||||
try: | |||||
try: | |||||
# _file_ = SCons.Util.to_str(_file_) | |||||
exec(compile(_file_.read(), _file_.name, 'exec'), | |||||
call_stack[-1].globals) | |||||
except SConscriptReturn: | |||||
pass | |||||
finally: | |||||
if old_file is not None: | |||||
call_stack[-1].globals.update({__file__:old_file}) | |||||
else: | |||||
SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, | |||||
"Ignoring missing SConscript '%s'" % f.get_internal_path()) | |||||
finally: | |||||
SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 | |||||
sys.path = old_sys_path | |||||
frame = call_stack.pop() | |||||
try: | |||||
fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) | |||||
except OSError: | |||||
# There was no local directory, so chdir to the | |||||
# Repository directory. Like above, we do this | |||||
# directly. | |||||
fs.chdir(frame.prev_dir, change_os_dir=0) | |||||
rdir = frame.prev_dir.rdir() | |||||
rdir._create() # Make sure there's a directory there. | |||||
try: | |||||
os.chdir(rdir.get_abspath()) | |||||
except OSError as e: | |||||
# We still couldn't chdir there, so raise the error, | |||||
# but only if actions are being executed. | |||||
# | |||||
# If the -n option was used, the directory would *not* | |||||
# have been created and we should just carry on and | |||||
# let things muddle through. This isn't guaranteed | |||||
# to work if the SConscript files are reading things | |||||
# from disk (for example), but it should work well | |||||
# enough for most configurations. | |||||
if SCons.Action.execute_actions: | |||||
raise e | |||||
results.append(frame.retval) | |||||
# if we only have one script, don't return a tuple | |||||
if len(results) == 1: | |||||
return results[0] | |||||
else: | |||||
return tuple(results) | |||||
def SConscript_exception(file=sys.stderr): | |||||
"""Print an exception stack trace just for the SConscript file(s). | |||||
This will show users who have Python errors where the problem is, | |||||
without cluttering the output with all of the internal calls leading | |||||
up to where we exec the SConscript.""" | |||||
exc_type, exc_value, exc_tb = sys.exc_info() | |||||
tb = exc_tb | |||||
while tb and stack_bottom not in tb.tb_frame.f_locals: | |||||
tb = tb.tb_next | |||||
if not tb: | |||||
# We did not find our exec statement, so this was actually a bug | |||||
# in SCons itself. Show the whole stack. | |||||
tb = exc_tb | |||||
stack = traceback.extract_tb(tb) | |||||
try: | |||||
type = exc_type.__name__ | |||||
except AttributeError: | |||||
type = str(exc_type) | |||||
if type[:11] == "exceptions.": | |||||
type = type[11:] | |||||
file.write('%s: %s:\n' % (type, exc_value)) | |||||
for fname, line, func, text in stack: | |||||
file.write(' File "%s", line %d:\n' % (fname, line)) | |||||
file.write(' %s\n' % text) | |||||
def annotate(node): | |||||
"""Annotate a node with the stack frame describing the | |||||
SConscript file and line number that created it.""" | |||||
tb = sys.exc_info()[2] | |||||
while tb and stack_bottom not in tb.tb_frame.f_locals: | |||||
tb = tb.tb_next | |||||
if not tb: | |||||
# We did not find any exec of an SConscript file: what?! | |||||
raise SCons.Errors.InternalError("could not find SConscript stack frame") | |||||
node.creator = traceback.extract_stack(tb)[0] | |||||
# The following line would cause each Node to be annotated using the | |||||
# above function. Unfortunately, this is a *huge* performance hit, so | |||||
# leave this disabled until we find a more efficient mechanism. | |||||
#SCons.Node.Annotate = annotate | |||||
class SConsEnvironment(SCons.Environment.Base): | |||||
"""An Environment subclass that contains all of the methods that | |||||
are particular to the wrapper SCons interface and which aren't | |||||
(or shouldn't be) part of the build engine itself. | |||||
Note that not all of the methods of this class have corresponding | |||||
global functions, there are some private methods. | |||||
""" | |||||
# | |||||
# Private methods of an SConsEnvironment. | |||||
# | |||||
def _exceeds_version(self, major, minor, v_major, v_minor): | |||||
"""Return 1 if 'major' and 'minor' are greater than the version | |||||
in 'v_major' and 'v_minor', and 0 otherwise.""" | |||||
return (major > v_major or (major == v_major and minor > v_minor)) | |||||
def _get_major_minor_revision(self, version_string): | |||||
"""Split a version string into major, minor and (optionally) | |||||
revision parts. | |||||
This is complicated by the fact that a version string can be | |||||
something like 3.2b1.""" | |||||
version = version_string.split(' ')[0].split('.') | |||||
v_major = int(version[0]) | |||||
v_minor = int(re.match('\d+', version[1]).group()) | |||||
if len(version) >= 3: | |||||
v_revision = int(re.match('\d+', version[2]).group()) | |||||
else: | |||||
v_revision = 0 | |||||
return v_major, v_minor, v_revision | |||||
def _get_SConscript_filenames(self, ls, kw): | |||||
""" | |||||
Convert the parameters passed to SConscript() calls into a list | |||||
of files and export variables. If the parameters are invalid, | |||||
throws SCons.Errors.UserError. Returns a tuple (l, e) where l | |||||
is a list of SConscript filenames and e is a list of exports. | |||||
""" | |||||
exports = [] | |||||
if len(ls) == 0: | |||||
try: | |||||
dirs = kw["dirs"] | |||||
except KeyError: | |||||
raise SCons.Errors.UserError("Invalid SConscript usage - no parameters") | |||||
if not SCons.Util.is_List(dirs): | |||||
dirs = [ dirs ] | |||||
dirs = list(map(str, dirs)) | |||||
name = kw.get('name', 'SConscript') | |||||
files = [os.path.join(n, name) for n in dirs] | |||||
elif len(ls) == 1: | |||||
files = ls[0] | |||||
elif len(ls) == 2: | |||||
files = ls[0] | |||||
exports = self.Split(ls[1]) | |||||
else: | |||||
raise SCons.Errors.UserError("Invalid SConscript() usage - too many arguments") | |||||
if not SCons.Util.is_List(files): | |||||
files = [ files ] | |||||
if kw.get('exports'): | |||||
exports.extend(self.Split(kw['exports'])) | |||||
variant_dir = kw.get('variant_dir') or kw.get('build_dir') | |||||
if variant_dir: | |||||
if len(files) != 1: | |||||
raise SCons.Errors.UserError("Invalid SConscript() usage - can only specify one SConscript with a variant_dir") | |||||
duplicate = kw.get('duplicate', 1) | |||||
src_dir = kw.get('src_dir') | |||||
if not src_dir: | |||||
src_dir, fname = os.path.split(str(files[0])) | |||||
files = [os.path.join(str(variant_dir), fname)] | |||||
else: | |||||
if not isinstance(src_dir, SCons.Node.Node): | |||||
src_dir = self.fs.Dir(src_dir) | |||||
fn = files[0] | |||||
if not isinstance(fn, SCons.Node.Node): | |||||
fn = self.fs.File(fn) | |||||
if fn.is_under(src_dir): | |||||
# Get path relative to the source directory. | |||||
fname = fn.get_path(src_dir) | |||||
files = [os.path.join(str(variant_dir), fname)] | |||||
else: | |||||
files = [fn.get_abspath()] | |||||
kw['src_dir'] = variant_dir | |||||
self.fs.VariantDir(variant_dir, src_dir, duplicate) | |||||
return (files, exports) | |||||
# | |||||
# Public methods of an SConsEnvironment. These get | |||||
# entry points in the global namespace so they can be called | |||||
# as global functions. | |||||
# | |||||
def Configure(self, *args, **kw): | |||||
if not SCons.Script.sconscript_reading: | |||||
raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") | |||||
kw['_depth'] = kw.get('_depth', 0) + 1 | |||||
return SCons.Environment.Base.Configure(self, *args, **kw) | |||||
def Default(self, *targets): | |||||
SCons.Script._Set_Default_Targets(self, targets) | |||||
def EnsureSConsVersion(self, major, minor, revision=0): | |||||
"""Exit abnormally if the SCons version is not late enough.""" | |||||
# split string to avoid replacement during build process | |||||
if SCons.__version__ == '__' + 'VERSION__': | |||||
SCons.Warnings.warn(SCons.Warnings.DevelopmentVersionWarning, | |||||
"EnsureSConsVersion is ignored for development version") | |||||
return | |||||
scons_ver = self._get_major_minor_revision(SCons.__version__) | |||||
if scons_ver < (major, minor, revision): | |||||
if revision: | |||||
scons_ver_string = '%d.%d.%d' % (major, minor, revision) | |||||
else: | |||||
scons_ver_string = '%d.%d' % (major, minor) | |||||
print("SCons %s or greater required, but you have SCons %s" % \ | |||||
(scons_ver_string, SCons.__version__)) | |||||
sys.exit(2) | |||||
def EnsurePythonVersion(self, major, minor): | |||||
"""Exit abnormally if the Python version is not late enough.""" | |||||
if sys.version_info < (major, minor): | |||||
v = sys.version.split()[0] | |||||
print("Python %d.%d or greater required, but you have Python %s" %(major,minor,v)) | |||||
sys.exit(2) | |||||
def Exit(self, value=0): | |||||
sys.exit(value) | |||||
def Export(self, *vars, **kw): | |||||
for var in vars: | |||||
global_exports.update(compute_exports(self.Split(var))) | |||||
global_exports.update(kw) | |||||
def GetLaunchDir(self): | |||||
global launch_dir | |||||
return launch_dir | |||||
def GetOption(self, name): | |||||
name = self.subst(name) | |||||
return SCons.Script.Main.GetOption(name) | |||||
def Help(self, text, append=False): | |||||
text = self.subst(text, raw=1) | |||||
SCons.Script.HelpFunction(text, append=append) | |||||
def Import(self, *vars): | |||||
try: | |||||
frame = call_stack[-1] | |||||
globals = frame.globals | |||||
exports = frame.exports | |||||
for var in vars: | |||||
var = self.Split(var) | |||||
for v in var: | |||||
if v == '*': | |||||
globals.update(global_exports) | |||||
globals.update(exports) | |||||
else: | |||||
if v in exports: | |||||
globals[v] = exports[v] | |||||
else: | |||||
globals[v] = global_exports[v] | |||||
except KeyError as x: | |||||
raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x) | |||||
def SConscript(self, *ls, **kw): | |||||
if 'build_dir' in kw: | |||||
msg = """The build_dir keyword has been deprecated; use the variant_dir keyword instead.""" | |||||
SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) | |||||
def subst_element(x, subst=self.subst): | |||||
if SCons.Util.is_List(x): | |||||
x = list(map(subst, x)) | |||||
else: | |||||
x = subst(x) | |||||
return x | |||||
ls = list(map(subst_element, ls)) | |||||
subst_kw = {} | |||||
for key, val in kw.items(): | |||||
if SCons.Util.is_String(val): | |||||
val = self.subst(val) | |||||
elif SCons.Util.is_List(val): | |||||
result = [] | |||||
for v in val: | |||||
if SCons.Util.is_String(v): | |||||
v = self.subst(v) | |||||
result.append(v) | |||||
val = result | |||||
subst_kw[key] = val | |||||
files, exports = self._get_SConscript_filenames(ls, subst_kw) | |||||
subst_kw['exports'] = exports | |||||
return _SConscript(self.fs, *files, **subst_kw) | |||||
def SConscriptChdir(self, flag): | |||||
global sconscript_chdir | |||||
sconscript_chdir = flag | |||||
def SetOption(self, name, value): | |||||
name = self.subst(name) | |||||
SCons.Script.Main.SetOption(name, value) | |||||
# | |||||
# | |||||
# | |||||
SCons.Environment.Environment = SConsEnvironment | |||||
def Configure(*args, **kw): | |||||
if not SCons.Script.sconscript_reading: | |||||
raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") | |||||
kw['_depth'] = 1 | |||||
return SCons.SConf.SConf(*args, **kw) | |||||
# It's very important that the DefaultEnvironmentCall() class stay in this | |||||
# file, with the get_calling_namespaces() function, the compute_exports() | |||||
# function, the Frame class and the SConsEnvironment.Export() method. | |||||
# These things make up the calling stack leading up to the actual global | |||||
# Export() or SConscript() call that the user issued. We want to allow | |||||
# users to export local variables that they define, like so: | |||||
# | |||||
# def func(): | |||||
# x = 1 | |||||
# Export('x') | |||||
# | |||||
# To support this, the get_calling_namespaces() function assumes that | |||||
# the *first* stack frame that's not from this file is the local frame | |||||
# for the Export() or SConscript() call. | |||||
_DefaultEnvironmentProxy = None | |||||
def get_DefaultEnvironmentProxy(): | |||||
global _DefaultEnvironmentProxy | |||||
if not _DefaultEnvironmentProxy: | |||||
default_env = SCons.Defaults.DefaultEnvironment() | |||||
_DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env) | |||||
return _DefaultEnvironmentProxy | |||||
class DefaultEnvironmentCall(object): | |||||
"""A class that implements "global function" calls of | |||||
Environment methods by fetching the specified method from the | |||||
DefaultEnvironment's class. Note that this uses an intermediate | |||||
proxy class instead of calling the DefaultEnvironment method | |||||
directly so that the proxy can override the subst() method and | |||||
thereby prevent expansion of construction variables (since from | |||||
the user's point of view this was called as a global function, | |||||
with no associated construction environment).""" | |||||
def __init__(self, method_name, subst=0): | |||||
self.method_name = method_name | |||||
if subst: | |||||
self.factory = SCons.Defaults.DefaultEnvironment | |||||
else: | |||||
self.factory = get_DefaultEnvironmentProxy | |||||
def __call__(self, *args, **kw): | |||||
env = self.factory() | |||||
method = getattr(env, self.method_name) | |||||
return method(*args, **kw) | |||||
def BuildDefaultGlobals(): | |||||
""" | |||||
Create a dictionary containing all the default globals for | |||||
SConstruct and SConscript files. | |||||
""" | |||||
global GlobalDict | |||||
if GlobalDict is None: | |||||
GlobalDict = {} | |||||
import SCons.Script | |||||
d = SCons.Script.__dict__ | |||||
def not_a_module(m, d=d, mtype=type(SCons.Script)): | |||||
return not isinstance(d[m], mtype) | |||||
for m in filter(not_a_module, dir(SCons.Script)): | |||||
GlobalDict[m] = d[m] | |||||
return GlobalDict.copy() | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,429 @@ | |||||
"""SCons.Script | |||||
This file implements the main() function used by the scons script. | |||||
Architecturally, this *is* the scons script, and will likely only be | |||||
called from the external "scons" wrapper. Consequently, anything here | |||||
should not be, or be considered, part of the build engine. If it's | |||||
something that we expect other software to want to use, it should go in | |||||
some other module. If it's specific to the "scons" script invocation, | |||||
it goes here. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Script/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import time | |||||
start_time = time.time() | |||||
import collections | |||||
import os | |||||
try: | |||||
from StringIO import StringIO | |||||
except ImportError: | |||||
from io import StringIO | |||||
import sys | |||||
# Special chicken-and-egg handling of the "--debug=memoizer" flag: | |||||
# | |||||
# SCons.Memoize contains a metaclass implementation that affects how | |||||
# the other classes are instantiated. The Memoizer may add shim methods | |||||
# to classes that have methods that cache computed values in order to | |||||
# count and report the hits and misses. | |||||
# | |||||
# If we wait to enable the Memoization until after we've parsed the | |||||
# command line options normally, it will be too late, because the Memoizer | |||||
# will have already analyzed the classes that it's Memoizing and decided | |||||
# to not add the shims. So we use a special-case, up-front check for | |||||
# the "--debug=memoizer" flag and enable Memoizer before we import any | |||||
# of the other modules that use it. | |||||
_args = sys.argv + os.environ.get('SCONSFLAGS', '').split() | |||||
if "--debug=memoizer" in _args: | |||||
import SCons.Memoize | |||||
import SCons.Warnings | |||||
try: | |||||
SCons.Memoize.EnableMemoization() | |||||
except SCons.Warnings.Warning: | |||||
# Some warning was thrown. Arrange for it to be displayed | |||||
# or not after warnings are configured. | |||||
from . import Main | |||||
exc_type, exc_value, tb = sys.exc_info() | |||||
Main.delayed_warnings.append((exc_type, exc_value)) | |||||
del _args | |||||
import SCons.Action | |||||
import SCons.Builder | |||||
import SCons.Environment | |||||
import SCons.Node.FS | |||||
import SCons.Options | |||||
import SCons.Platform | |||||
import SCons.Scanner | |||||
import SCons.SConf | |||||
import SCons.Subst | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
import SCons.Variables | |||||
import SCons.Defaults | |||||
from . import Main | |||||
main = Main.main | |||||
# The following are global class definitions and variables that used to | |||||
# live directly in this module back before 0.96.90, when it contained | |||||
# a lot of code. Some SConscript files in widely-distributed packages | |||||
# (Blender is the specific example) actually reached into SCons.Script | |||||
# directly to use some of these. Rather than break those SConscript | |||||
# files, we're going to propagate these names into the SCons.Script | |||||
# namespace here. | |||||
# | |||||
# Some of these are commented out because it's *really* unlikely anyone | |||||
# used them, but we're going to leave the comment here to try to make | |||||
# it obvious what to do if the situation arises. | |||||
BuildTask = Main.BuildTask | |||||
CleanTask = Main.CleanTask | |||||
QuestionTask = Main.QuestionTask | |||||
#PrintHelp = Main.PrintHelp | |||||
#SConscriptSettableOptions = Main.SConscriptSettableOptions | |||||
AddOption = Main.AddOption | |||||
PrintHelp = Main.PrintHelp | |||||
GetOption = Main.GetOption | |||||
SetOption = Main.SetOption | |||||
Progress = Main.Progress | |||||
GetBuildFailures = Main.GetBuildFailures | |||||
#keep_going_on_error = Main.keep_going_on_error | |||||
#print_dtree = Main.print_dtree | |||||
#print_explanations = Main.print_explanations | |||||
#print_includes = Main.print_includes | |||||
#print_objects = Main.print_objects | |||||
#print_time = Main.print_time | |||||
#print_tree = Main.print_tree | |||||
#memory_stats = Main.memory_stats | |||||
#ignore_errors = Main.ignore_errors | |||||
#sconscript_time = Main.sconscript_time | |||||
#command_time = Main.command_time | |||||
#exit_status = Main.exit_status | |||||
#profiling = Main.profiling | |||||
#repositories = Main.repositories | |||||
# | |||||
from . import SConscript | |||||
_SConscript = SConscript | |||||
call_stack = _SConscript.call_stack | |||||
# | |||||
Action = SCons.Action.Action | |||||
AddMethod = SCons.Util.AddMethod | |||||
AllowSubstExceptions = SCons.Subst.SetAllowableExceptions | |||||
Builder = SCons.Builder.Builder | |||||
Configure = _SConscript.Configure | |||||
Environment = SCons.Environment.Environment | |||||
#OptParser = SCons.SConsOptions.OptParser | |||||
FindPathDirs = SCons.Scanner.FindPathDirs | |||||
Platform = SCons.Platform.Platform | |||||
Return = _SConscript.Return | |||||
Scanner = SCons.Scanner.Base | |||||
Tool = SCons.Tool.Tool | |||||
WhereIs = SCons.Util.WhereIs | |||||
# | |||||
BoolVariable = SCons.Variables.BoolVariable | |||||
EnumVariable = SCons.Variables.EnumVariable | |||||
ListVariable = SCons.Variables.ListVariable | |||||
PackageVariable = SCons.Variables.PackageVariable | |||||
PathVariable = SCons.Variables.PathVariable | |||||
# Deprecated names that will go away some day. | |||||
BoolOption = SCons.Options.BoolOption | |||||
EnumOption = SCons.Options.EnumOption | |||||
ListOption = SCons.Options.ListOption | |||||
PackageOption = SCons.Options.PackageOption | |||||
PathOption = SCons.Options.PathOption | |||||
# Action factories. | |||||
Chmod = SCons.Defaults.Chmod | |||||
Copy = SCons.Defaults.Copy | |||||
Delete = SCons.Defaults.Delete | |||||
Mkdir = SCons.Defaults.Mkdir | |||||
Move = SCons.Defaults.Move | |||||
Touch = SCons.Defaults.Touch | |||||
# Pre-made, public scanners. | |||||
CScanner = SCons.Tool.CScanner | |||||
DScanner = SCons.Tool.DScanner | |||||
DirScanner = SCons.Defaults.DirScanner | |||||
ProgramScanner = SCons.Tool.ProgramScanner | |||||
SourceFileScanner = SCons.Tool.SourceFileScanner | |||||
# Functions we might still convert to Environment methods. | |||||
CScan = SCons.Defaults.CScan | |||||
DefaultEnvironment = SCons.Defaults.DefaultEnvironment | |||||
# Other variables we provide. | |||||
class TargetList(collections.UserList): | |||||
def _do_nothing(self, *args, **kw): | |||||
pass | |||||
def _add_Default(self, list): | |||||
self.extend(list) | |||||
def _clear(self): | |||||
del self[:] | |||||
ARGUMENTS = {} | |||||
ARGLIST = [] | |||||
BUILD_TARGETS = TargetList() | |||||
COMMAND_LINE_TARGETS = [] | |||||
DEFAULT_TARGETS = [] | |||||
# BUILD_TARGETS can be modified in the SConscript files. If so, we | |||||
# want to treat the modified BUILD_TARGETS list as if they specified | |||||
# targets on the command line. To do that, though, we need to know if | |||||
# BUILD_TARGETS was modified through "official" APIs or by hand. We do | |||||
# this by updating two lists in parallel, the documented BUILD_TARGETS | |||||
# list, above, and this internal _build_plus_default targets list which | |||||
# should only have "official" API changes. Then Script/Main.py can | |||||
# compare these two afterwards to figure out if the user added their | |||||
# own targets to BUILD_TARGETS. | |||||
_build_plus_default = TargetList() | |||||
def _Add_Arguments(alist): | |||||
for arg in alist: | |||||
a, b = arg.split('=', 1) | |||||
ARGUMENTS[a] = b | |||||
ARGLIST.append((a, b)) | |||||
def _Add_Targets(tlist): | |||||
if tlist: | |||||
COMMAND_LINE_TARGETS.extend(tlist) | |||||
BUILD_TARGETS.extend(tlist) | |||||
BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing | |||||
BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing | |||||
_build_plus_default.extend(tlist) | |||||
_build_plus_default._add_Default = _build_plus_default._do_nothing | |||||
_build_plus_default._clear = _build_plus_default._do_nothing | |||||
def _Set_Default_Targets_Has_Been_Called(d, fs): | |||||
return DEFAULT_TARGETS | |||||
def _Set_Default_Targets_Has_Not_Been_Called(d, fs): | |||||
if d is None: | |||||
d = [fs.Dir('.')] | |||||
return d | |||||
_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called | |||||
def _Set_Default_Targets(env, tlist): | |||||
global DEFAULT_TARGETS | |||||
global _Get_Default_Targets | |||||
_Get_Default_Targets = _Set_Default_Targets_Has_Been_Called | |||||
for t in tlist: | |||||
if t is None: | |||||
# Delete the elements from the list in-place, don't | |||||
# reassign an empty list to DEFAULT_TARGETS, so that the | |||||
# variables will still point to the same object we point to. | |||||
del DEFAULT_TARGETS[:] | |||||
BUILD_TARGETS._clear() | |||||
_build_plus_default._clear() | |||||
elif isinstance(t, SCons.Node.Node): | |||||
DEFAULT_TARGETS.append(t) | |||||
BUILD_TARGETS._add_Default([t]) | |||||
_build_plus_default._add_Default([t]) | |||||
else: | |||||
nodes = env.arg2nodes(t, env.fs.Entry) | |||||
DEFAULT_TARGETS.extend(nodes) | |||||
BUILD_TARGETS._add_Default(nodes) | |||||
_build_plus_default._add_Default(nodes) | |||||
# | |||||
help_text = None | |||||
def HelpFunction(text, append=False): | |||||
global help_text | |||||
if help_text is None: | |||||
if append: | |||||
s = StringIO() | |||||
PrintHelp(s) | |||||
help_text = s.getvalue() | |||||
s.close() | |||||
else: | |||||
help_text = "" | |||||
help_text= help_text + text | |||||
# | |||||
# Will be non-zero if we are reading an SConscript file. | |||||
sconscript_reading = 0 | |||||
# | |||||
def Variables(files=[], args=ARGUMENTS): | |||||
return SCons.Variables.Variables(files, args) | |||||
def Options(files=[], args=ARGUMENTS): | |||||
return SCons.Options.Options(files, args) | |||||
# The list of global functions to add to the SConscript name space | |||||
# that end up calling corresponding methods or Builders in the | |||||
# DefaultEnvironment(). | |||||
GlobalDefaultEnvironmentFunctions = [ | |||||
# Methods from the SConsEnvironment class, above. | |||||
'Default', | |||||
'EnsurePythonVersion', | |||||
'EnsureSConsVersion', | |||||
'Exit', | |||||
'Export', | |||||
'GetLaunchDir', | |||||
'Help', | |||||
'Import', | |||||
#'SConscript', is handled separately, below. | |||||
'SConscriptChdir', | |||||
# Methods from the Environment.Base class. | |||||
'AddPostAction', | |||||
'AddPreAction', | |||||
'Alias', | |||||
'AlwaysBuild', | |||||
'BuildDir', | |||||
'CacheDir', | |||||
'Clean', | |||||
#The Command() method is handled separately, below. | |||||
'Decider', | |||||
'Depends', | |||||
'Dir', | |||||
'NoClean', | |||||
'NoCache', | |||||
'Entry', | |||||
'Execute', | |||||
'File', | |||||
'FindFile', | |||||
'FindInstalledFiles', | |||||
'FindSourceFiles', | |||||
'Flatten', | |||||
'GetBuildPath', | |||||
'Glob', | |||||
'Ignore', | |||||
'Install', | |||||
'InstallAs', | |||||
'InstallVersionedLib', | |||||
'Literal', | |||||
'Local', | |||||
'ParseDepends', | |||||
'Precious', | |||||
'PyPackageDir', | |||||
'Repository', | |||||
'Requires', | |||||
'SConsignFile', | |||||
'SideEffect', | |||||
'SourceCode', | |||||
'SourceSignatures', | |||||
'Split', | |||||
'Tag', | |||||
'TargetSignatures', | |||||
'Value', | |||||
'VariantDir', | |||||
] | |||||
GlobalDefaultBuilders = [ | |||||
# Supported builders. | |||||
'CFile', | |||||
'CXXFile', | |||||
'DVI', | |||||
'Jar', | |||||
'Java', | |||||
'JavaH', | |||||
'Library', | |||||
'LoadableModule', | |||||
'M4', | |||||
'MSVSProject', | |||||
'Object', | |||||
'PCH', | |||||
'PDF', | |||||
'PostScript', | |||||
'Program', | |||||
'RES', | |||||
'RMIC', | |||||
'SharedLibrary', | |||||
'SharedObject', | |||||
'StaticLibrary', | |||||
'StaticObject', | |||||
'Tar', | |||||
'TypeLibrary', | |||||
'Zip', | |||||
'Package', | |||||
] | |||||
for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: | |||||
exec ("%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name))) | |||||
del name | |||||
# There are a handful of variables that used to live in the | |||||
# Script/SConscript.py module that some SConscript files out there were | |||||
# accessing directly as SCons.Script.SConscript.*. The problem is that | |||||
# "SConscript" in this namespace is no longer a module, it's a global | |||||
# function call--or more precisely, an object that implements a global | |||||
# function call through the default Environment. Nevertheless, we can | |||||
# maintain backwards compatibility for SConscripts that were reaching in | |||||
# this way by hanging some attributes off the "SConscript" object here. | |||||
SConscript = _SConscript.DefaultEnvironmentCall('SConscript') | |||||
# Make SConscript look enough like the module it used to be so | |||||
# that pychecker doesn't barf. | |||||
SConscript.__name__ = 'SConscript' | |||||
SConscript.Arguments = ARGUMENTS | |||||
SConscript.ArgList = ARGLIST | |||||
SConscript.BuildTargets = BUILD_TARGETS | |||||
SConscript.CommandLineTargets = COMMAND_LINE_TARGETS | |||||
SConscript.DefaultTargets = DEFAULT_TARGETS | |||||
# The global Command() function must be handled differently than the | |||||
# global functions for other construction environment methods because | |||||
# we want people to be able to use Actions that must expand $TARGET | |||||
# and $SOURCE later, when (and if) the Action is invoked to build | |||||
# the target(s). We do this with the subst=1 argument, which creates | |||||
# a DefaultEnvironmentCall instance that wraps up a normal default | |||||
# construction environment that performs variable substitution, not a | |||||
# proxy that doesn't. | |||||
# | |||||
# There's a flaw here, though, because any other $-variables on a command | |||||
# line will *also* be expanded, each to a null string, but that should | |||||
# only be a problem in the unusual case where someone was passing a '$' | |||||
# on a command line and *expected* the $ to get through to the shell | |||||
# because they were calling Command() and not env.Command()... This is | |||||
# unlikely enough that we're going to leave this as is and cross that | |||||
# bridge if someone actually comes to it. | |||||
Command = _SConscript.DefaultEnvironmentCall('Command', subst=1) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,916 @@ | |||||
"""SCons.Subst | |||||
SCons string substitution. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Subst.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import collections | |||||
import re | |||||
import SCons.Errors | |||||
from SCons.Util import is_String, is_Sequence | |||||
# Indexed by the SUBST_* constants below. | |||||
_strconv = [SCons.Util.to_String_for_subst, | |||||
SCons.Util.to_String_for_subst, | |||||
SCons.Util.to_String_for_signature] | |||||
AllowableExceptions = (IndexError, NameError) | |||||
def SetAllowableExceptions(*excepts): | |||||
global AllowableExceptions | |||||
AllowableExceptions = [_f for _f in excepts if _f] | |||||
def raise_exception(exception, target, s): | |||||
name = exception.__class__.__name__ | |||||
msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s) | |||||
if target: | |||||
raise SCons.Errors.BuildError(target[0], msg) | |||||
else: | |||||
raise SCons.Errors.UserError(msg) | |||||
class Literal(object): | |||||
"""A wrapper for a string. If you use this object wrapped | |||||
around a string, then it will be interpreted as literal. | |||||
When passed to the command interpreter, all special | |||||
characters will be escaped.""" | |||||
def __init__(self, lstr): | |||||
self.lstr = lstr | |||||
def __str__(self): | |||||
return self.lstr | |||||
def escape(self, escape_func): | |||||
return escape_func(self.lstr) | |||||
def for_signature(self): | |||||
return self.lstr | |||||
def is_literal(self): | |||||
return 1 | |||||
def __eq__(self, other): | |||||
if not isinstance(other, Literal): | |||||
return False | |||||
return self.lstr == other.lstr | |||||
def __neq__(self, other): | |||||
return not self.__eq__(other) | |||||
class SpecialAttrWrapper(object): | |||||
"""This is a wrapper for what we call a 'Node special attribute.' | |||||
This is any of the attributes of a Node that we can reference from | |||||
Environment variable substitution, such as $TARGET.abspath or | |||||
$SOURCES[1].filebase. We implement the same methods as Literal | |||||
so we can handle special characters, plus a for_signature method, | |||||
such that we can return some canonical string during signature | |||||
calculation to avoid unnecessary rebuilds.""" | |||||
def __init__(self, lstr, for_signature=None): | |||||
"""The for_signature parameter, if supplied, will be the | |||||
canonical string we return from for_signature(). Else | |||||
we will simply return lstr.""" | |||||
self.lstr = lstr | |||||
if for_signature: | |||||
self.forsig = for_signature | |||||
else: | |||||
self.forsig = lstr | |||||
def __str__(self): | |||||
return self.lstr | |||||
def escape(self, escape_func): | |||||
return escape_func(self.lstr) | |||||
def for_signature(self): | |||||
return self.forsig | |||||
def is_literal(self): | |||||
return 1 | |||||
def quote_spaces(arg): | |||||
"""Generic function for putting double quotes around any string that | |||||
has white space in it.""" | |||||
if ' ' in arg or '\t' in arg: | |||||
return '"%s"' % arg | |||||
else: | |||||
return str(arg) | |||||
class CmdStringHolder(collections.UserString): | |||||
"""This is a special class used to hold strings generated by | |||||
scons_subst() and scons_subst_list(). It defines a special method | |||||
escape(). When passed a function with an escape algorithm for a | |||||
particular platform, it will return the contained string with the | |||||
proper escape sequences inserted. | |||||
""" | |||||
def __init__(self, cmd, literal=None): | |||||
collections.UserString.__init__(self, cmd) | |||||
self.literal = literal | |||||
def is_literal(self): | |||||
return self.literal | |||||
def escape(self, escape_func, quote_func=quote_spaces): | |||||
"""Escape the string with the supplied function. The | |||||
function is expected to take an arbitrary string, then | |||||
return it with all special characters escaped and ready | |||||
for passing to the command interpreter. | |||||
After calling this function, the next call to str() will | |||||
return the escaped string. | |||||
""" | |||||
if self.is_literal(): | |||||
return escape_func(self.data) | |||||
elif ' ' in self.data or '\t' in self.data: | |||||
return quote_func(self.data) | |||||
else: | |||||
return self.data | |||||
def escape_list(mylist, escape_func): | |||||
"""Escape a list of arguments by running the specified escape_func | |||||
on every object in the list that has an escape() method.""" | |||||
def escape(obj, escape_func=escape_func): | |||||
try: | |||||
e = obj.escape | |||||
except AttributeError: | |||||
return obj | |||||
else: | |||||
return e(escape_func) | |||||
return list(map(escape, mylist)) | |||||
class NLWrapper(object): | |||||
"""A wrapper class that delays turning a list of sources or targets | |||||
into a NodeList until it's needed. The specified function supplied | |||||
when the object is initialized is responsible for turning raw nodes | |||||
into proxies that implement the special attributes like .abspath, | |||||
.source, etc. This way, we avoid creating those proxies just | |||||
"in case" someone is going to use $TARGET or the like, and only | |||||
go through the trouble if we really have to. | |||||
In practice, this might be a wash performance-wise, but it's a little | |||||
cleaner conceptually... | |||||
""" | |||||
def __init__(self, list, func): | |||||
self.list = list | |||||
self.func = func | |||||
def _return_nodelist(self): | |||||
return self.nodelist | |||||
def _gen_nodelist(self): | |||||
mylist = self.list | |||||
if mylist is None: | |||||
mylist = [] | |||||
elif not is_Sequence(mylist): | |||||
mylist = [mylist] | |||||
# The map(self.func) call is what actually turns | |||||
# a list into appropriate proxies. | |||||
self.nodelist = SCons.Util.NodeList(list(map(self.func, mylist))) | |||||
self._create_nodelist = self._return_nodelist | |||||
return self.nodelist | |||||
_create_nodelist = _gen_nodelist | |||||
class Targets_or_Sources(collections.UserList): | |||||
"""A class that implements $TARGETS or $SOURCES expansions by in turn | |||||
wrapping a NLWrapper. This class handles the different methods used | |||||
to access the list, calling the NLWrapper to create proxies on demand. | |||||
Note that we subclass collections.UserList purely so that the | |||||
is_Sequence() function will identify an object of this class as | |||||
a list during variable expansion. We're not really using any | |||||
collections.UserList methods in practice. | |||||
""" | |||||
def __init__(self, nl): | |||||
self.nl = nl | |||||
def __getattr__(self, attr): | |||||
nl = self.nl._create_nodelist() | |||||
return getattr(nl, attr) | |||||
def __getitem__(self, i): | |||||
nl = self.nl._create_nodelist() | |||||
return nl[i] | |||||
def __getslice__(self, i, j): | |||||
nl = self.nl._create_nodelist() | |||||
i = max(i, 0); j = max(j, 0) | |||||
return nl[i:j] | |||||
def __str__(self): | |||||
nl = self.nl._create_nodelist() | |||||
return str(nl) | |||||
def __repr__(self): | |||||
nl = self.nl._create_nodelist() | |||||
return repr(nl) | |||||
class Target_or_Source(object): | |||||
"""A class that implements $TARGET or $SOURCE expansions by in turn | |||||
wrapping a NLWrapper. This class handles the different methods used | |||||
to access an individual proxy Node, calling the NLWrapper to create | |||||
a proxy on demand. | |||||
""" | |||||
def __init__(self, nl): | |||||
self.nl = nl | |||||
def __getattr__(self, attr): | |||||
nl = self.nl._create_nodelist() | |||||
try: | |||||
nl0 = nl[0] | |||||
except IndexError: | |||||
# If there is nothing in the list, then we have no attributes to | |||||
# pass through, so raise AttributeError for everything. | |||||
raise AttributeError("NodeList has no attribute: %s" % attr) | |||||
return getattr(nl0, attr) | |||||
def __str__(self): | |||||
nl = self.nl._create_nodelist() | |||||
if nl: | |||||
return str(nl[0]) | |||||
return '' | |||||
def __repr__(self): | |||||
nl = self.nl._create_nodelist() | |||||
if nl: | |||||
return repr(nl[0]) | |||||
return '' | |||||
class NullNodeList(SCons.Util.NullSeq): | |||||
def __call__(self, *args, **kwargs): return '' | |||||
def __str__(self): return '' | |||||
NullNodesList = NullNodeList() | |||||
def subst_dict(target, source): | |||||
"""Create a dictionary for substitution of special | |||||
construction variables. | |||||
This translates the following special arguments: | |||||
target - the target (object or array of objects), | |||||
used to generate the TARGET and TARGETS | |||||
construction variables | |||||
source - the source (object or array of objects), | |||||
used to generate the SOURCES and SOURCE | |||||
construction variables | |||||
""" | |||||
dict = {} | |||||
if target: | |||||
def get_tgt_subst_proxy(thing): | |||||
try: | |||||
subst_proxy = thing.get_subst_proxy() | |||||
except AttributeError: | |||||
subst_proxy = thing # probably a string, just return it | |||||
return subst_proxy | |||||
tnl = NLWrapper(target, get_tgt_subst_proxy) | |||||
dict['TARGETS'] = Targets_or_Sources(tnl) | |||||
dict['TARGET'] = Target_or_Source(tnl) | |||||
# This is a total cheat, but hopefully this dictionary goes | |||||
# away soon anyway. We just let these expand to $TARGETS | |||||
# because that's "good enough" for the use of ToolSurrogates | |||||
# (see test/ToolSurrogate.py) to generate documentation. | |||||
dict['CHANGED_TARGETS'] = '$TARGETS' | |||||
dict['UNCHANGED_TARGETS'] = '$TARGETS' | |||||
else: | |||||
dict['TARGETS'] = NullNodesList | |||||
dict['TARGET'] = NullNodesList | |||||
if source: | |||||
def get_src_subst_proxy(node): | |||||
try: | |||||
rfile = node.rfile | |||||
except AttributeError: | |||||
pass | |||||
else: | |||||
node = rfile() | |||||
try: | |||||
return node.get_subst_proxy() | |||||
except AttributeError: | |||||
return node # probably a String, just return it | |||||
snl = NLWrapper(source, get_src_subst_proxy) | |||||
dict['SOURCES'] = Targets_or_Sources(snl) | |||||
dict['SOURCE'] = Target_or_Source(snl) | |||||
# This is a total cheat, but hopefully this dictionary goes | |||||
# away soon anyway. We just let these expand to $TARGETS | |||||
# because that's "good enough" for the use of ToolSurrogates | |||||
# (see test/ToolSurrogate.py) to generate documentation. | |||||
dict['CHANGED_SOURCES'] = '$SOURCES' | |||||
dict['UNCHANGED_SOURCES'] = '$SOURCES' | |||||
else: | |||||
dict['SOURCES'] = NullNodesList | |||||
dict['SOURCE'] = NullNodesList | |||||
return dict | |||||
# Constants for the "mode" parameter to scons_subst_list() and | |||||
# scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD | |||||
# gives a command line suitable for passing to a shell. SUBST_SIG | |||||
# gives a command line appropriate for calculating the signature | |||||
# of a command line...if this changes, we should rebuild. | |||||
SUBST_CMD = 0 | |||||
SUBST_RAW = 1 | |||||
SUBST_SIG = 2 | |||||
_rm = re.compile(r'\$[()]') | |||||
_rm_split = re.compile(r'(\$[()])') | |||||
# Indexed by the SUBST_* constants above. | |||||
_regex_remove = [ _rm, None, _rm_split ] | |||||
def _rm_list(list): | |||||
return [l for l in list if not l in ('$(', '$)')] | |||||
def _remove_list(list): | |||||
result = [] | |||||
depth = 0 | |||||
for l in list: | |||||
if l == '$(': | |||||
depth += 1 | |||||
elif l == '$)': | |||||
depth -= 1 | |||||
if depth < 0: | |||||
break | |||||
elif depth == 0: | |||||
result.append(l) | |||||
if depth != 0: | |||||
return None | |||||
return result | |||||
# Indexed by the SUBST_* constants above. | |||||
_list_remove = [ _rm_list, None, _remove_list ] | |||||
# Regular expressions for splitting strings and handling substitutions, | |||||
# for use by the scons_subst() and scons_subst_list() functions: | |||||
# | |||||
# The first expression compiled matches all of the $-introduced tokens | |||||
# that we need to process in some way, and is used for substitutions. | |||||
# The expressions it matches are: | |||||
# | |||||
# "$$" | |||||
# "$(" | |||||
# "$)" | |||||
# "$variable" [must begin with alphabetic or underscore] | |||||
# "${any stuff}" | |||||
# | |||||
# The second expression compiled is used for splitting strings into tokens | |||||
# to be processed, and it matches all of the tokens listed above, plus | |||||
# the following that affect how arguments do or don't get joined together: | |||||
# | |||||
# " " [white space] | |||||
# "non-white-space" [without any dollar signs] | |||||
# "$" [single dollar sign] | |||||
# | |||||
_dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}' | |||||
_dollar_exps = re.compile(r'(%s)' % _dollar_exps_str) | |||||
_separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str) | |||||
# This regular expression is used to replace strings of multiple white | |||||
# space characters in the string result from the scons_subst() function. | |||||
_space_sep = re.compile(r'[\t ]+(?![^{]*})') | |||||
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): | |||||
"""Expand a string or list containing construction variable | |||||
substitutions. | |||||
This is the work-horse function for substitutions in file names | |||||
and the like. The companion scons_subst_list() function (below) | |||||
handles separating command lines into lists of arguments, so see | |||||
that function if that's what you're looking for. | |||||
""" | |||||
if isinstance(strSubst, str) and strSubst.find('$') < 0: | |||||
return strSubst | |||||
class StringSubber(object): | |||||
"""A class to construct the results of a scons_subst() call. | |||||
This binds a specific construction environment, mode, target and | |||||
source with two methods (substitute() and expand()) that handle | |||||
the expansion. | |||||
""" | |||||
def __init__(self, env, mode, conv, gvars): | |||||
self.env = env | |||||
self.mode = mode | |||||
self.conv = conv | |||||
self.gvars = gvars | |||||
def expand(self, s, lvars): | |||||
"""Expand a single "token" as necessary, returning an | |||||
appropriate string containing the expansion. | |||||
This handles expanding different types of things (strings, | |||||
lists, callables) appropriately. It calls the wrapper | |||||
substitute() method to re-expand things as necessary, so that | |||||
the results of expansions of side-by-side strings still get | |||||
re-evaluated separately, not smushed together. | |||||
""" | |||||
if is_String(s): | |||||
try: | |||||
s0, s1 = s[:2] | |||||
except (IndexError, ValueError): | |||||
return s | |||||
if s0 != '$': | |||||
return s | |||||
if s1 == '$': | |||||
return '$' | |||||
elif s1 in '()': | |||||
return s | |||||
else: | |||||
key = s[1:] | |||||
if key[0] == '{' or '.' in key: | |||||
if key[0] == '{': | |||||
key = key[1:-1] | |||||
try: | |||||
s = eval(key, self.gvars, lvars) | |||||
except KeyboardInterrupt: | |||||
raise | |||||
except Exception as e: | |||||
if e.__class__ in AllowableExceptions: | |||||
return '' | |||||
raise_exception(e, lvars['TARGETS'], s) | |||||
else: | |||||
if key in lvars: | |||||
s = lvars[key] | |||||
elif key in self.gvars: | |||||
s = self.gvars[key] | |||||
elif not NameError in AllowableExceptions: | |||||
raise_exception(NameError(key), lvars['TARGETS'], s) | |||||
else: | |||||
return '' | |||||
# Before re-expanding the result, handle | |||||
# recursive expansion by copying the local | |||||
# variable dictionary and overwriting a null | |||||
# string for the value of the variable name | |||||
# we just expanded. | |||||
# | |||||
# This could potentially be optimized by only | |||||
# copying lvars when s contains more expansions, | |||||
# but lvars is usually supposed to be pretty | |||||
# small, and deeply nested variable expansions | |||||
# are probably more the exception than the norm, | |||||
# so it should be tolerable for now. | |||||
lv = lvars.copy() | |||||
var = key.split('.')[0] | |||||
lv[var] = '' | |||||
return self.substitute(s, lv) | |||||
elif is_Sequence(s): | |||||
def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): | |||||
return conv(substitute(l, lvars)) | |||||
return list(map(func, s)) | |||||
elif callable(s): | |||||
try: | |||||
s = s(target=lvars['TARGETS'], | |||||
source=lvars['SOURCES'], | |||||
env=self.env, | |||||
for_signature=(self.mode != SUBST_CMD)) | |||||
except TypeError: | |||||
# This probably indicates that it's a callable | |||||
# object that doesn't match our calling arguments | |||||
# (like an Action). | |||||
if self.mode == SUBST_RAW: | |||||
return s | |||||
s = self.conv(s) | |||||
return self.substitute(s, lvars) | |||||
elif s is None: | |||||
return '' | |||||
else: | |||||
return s | |||||
def substitute(self, args, lvars): | |||||
"""Substitute expansions in an argument or list of arguments. | |||||
This serves as a wrapper for splitting up a string into | |||||
separate tokens. | |||||
""" | |||||
if is_String(args) and not isinstance(args, CmdStringHolder): | |||||
args = str(args) # In case it's a UserString. | |||||
try: | |||||
def sub_match(match): | |||||
return self.conv(self.expand(match.group(1), lvars)) | |||||
result = _dollar_exps.sub(sub_match, args) | |||||
except TypeError: | |||||
# If the internal conversion routine doesn't return | |||||
# strings (it could be overridden to return Nodes, for | |||||
# example), then the 1.5.2 re module will throw this | |||||
# exception. Back off to a slower, general-purpose | |||||
# algorithm that works for all data types. | |||||
args = _separate_args.findall(args) | |||||
result = [] | |||||
for a in args: | |||||
result.append(self.conv(self.expand(a, lvars))) | |||||
if len(result) == 1: | |||||
result = result[0] | |||||
else: | |||||
result = ''.join(map(str, result)) | |||||
return result | |||||
else: | |||||
return self.expand(args, lvars) | |||||
if conv is None: | |||||
conv = _strconv[mode] | |||||
# Doing this every time is a bit of a waste, since the Executor | |||||
# has typically already populated the OverrideEnvironment with | |||||
# $TARGET/$SOURCE variables. We're keeping this (for now), though, | |||||
# because it supports existing behavior that allows us to call | |||||
# an Action directly with an arbitrary target+source pair, which | |||||
# we use in Tool/tex.py to handle calling $BIBTEX when necessary. | |||||
# If we dropped that behavior (or found another way to cover it), | |||||
# we could get rid of this call completely and just rely on the | |||||
# Executor setting the variables. | |||||
if 'TARGET' not in lvars: | |||||
d = subst_dict(target, source) | |||||
if d: | |||||
lvars = lvars.copy() | |||||
lvars.update(d) | |||||
# We're (most likely) going to eval() things. If Python doesn't | |||||
# find a __builtins__ value in the global dictionary used for eval(), | |||||
# it copies the current global values for you. Avoid this by | |||||
# setting it explicitly and then deleting, so we don't pollute the | |||||
# construction environment Dictionary(ies) that are typically used | |||||
# for expansion. | |||||
gvars['__builtins__'] = __builtins__ | |||||
ss = StringSubber(env, mode, conv, gvars) | |||||
result = ss.substitute(strSubst, lvars) | |||||
try: | |||||
del gvars['__builtins__'] | |||||
except KeyError: | |||||
pass | |||||
res = result | |||||
if is_String(result): | |||||
# Remove $(-$) pairs and any stuff in between, | |||||
# if that's appropriate. | |||||
remove = _regex_remove[mode] | |||||
if remove: | |||||
if mode == SUBST_SIG: | |||||
result = _list_remove[mode](remove.split(result)) | |||||
if result is None: | |||||
raise SCons.Errors.UserError("Unbalanced $(/$) in: " + res) | |||||
result = ' '.join(result) | |||||
else: | |||||
result = remove.sub('', result) | |||||
if mode != SUBST_RAW: | |||||
# Compress strings of white space characters into | |||||
# a single space. | |||||
result = _space_sep.sub(' ', result).strip() | |||||
elif is_Sequence(result): | |||||
remove = _list_remove[mode] | |||||
if remove: | |||||
result = remove(result) | |||||
if result is None: | |||||
raise SCons.Errors.UserError("Unbalanced $(/$) in: " + str(res)) | |||||
return result | |||||
def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): | |||||
"""Substitute construction variables in a string (or list or other | |||||
object) and separate the arguments into a command list. | |||||
The companion scons_subst() function (above) handles basic | |||||
substitutions within strings, so see that function instead | |||||
if that's what you're looking for. | |||||
""" | |||||
class ListSubber(collections.UserList): | |||||
"""A class to construct the results of a scons_subst_list() call. | |||||
Like StringSubber, this class binds a specific construction | |||||
environment, mode, target and source with two methods | |||||
(substitute() and expand()) that handle the expansion. | |||||
In addition, however, this class is used to track the state of | |||||
the result(s) we're gathering so we can do the appropriate thing | |||||
whenever we have to append another word to the result--start a new | |||||
line, start a new word, append to the current word, etc. We do | |||||
this by setting the "append" attribute to the right method so | |||||
that our wrapper methods only need ever call ListSubber.append(), | |||||
and the rest of the object takes care of doing the right thing | |||||
internally. | |||||
""" | |||||
def __init__(self, env, mode, conv, gvars): | |||||
collections.UserList.__init__(self, []) | |||||
self.env = env | |||||
self.mode = mode | |||||
self.conv = conv | |||||
self.gvars = gvars | |||||
if self.mode == SUBST_RAW: | |||||
self.add_strip = lambda x: self.append(x) | |||||
else: | |||||
self.add_strip = lambda x: None | |||||
self.in_strip = None | |||||
self.next_line() | |||||
def expand(self, s, lvars, within_list): | |||||
"""Expand a single "token" as necessary, appending the | |||||
expansion to the current result. | |||||
This handles expanding different types of things (strings, | |||||
lists, callables) appropriately. It calls the wrapper | |||||
substitute() method to re-expand things as necessary, so that | |||||
the results of expansions of side-by-side strings still get | |||||
re-evaluated separately, not smushed together. | |||||
""" | |||||
if is_String(s): | |||||
try: | |||||
s0, s1 = s[:2] | |||||
except (IndexError, ValueError): | |||||
self.append(s) | |||||
return | |||||
if s0 != '$': | |||||
self.append(s) | |||||
return | |||||
if s1 == '$': | |||||
self.append('$') | |||||
elif s1 == '(': | |||||
self.open_strip('$(') | |||||
elif s1 == ')': | |||||
self.close_strip('$)') | |||||
else: | |||||
key = s[1:] | |||||
if key[0] == '{' or key.find('.') >= 0: | |||||
if key[0] == '{': | |||||
key = key[1:-1] | |||||
try: | |||||
s = eval(key, self.gvars, lvars) | |||||
except KeyboardInterrupt: | |||||
raise | |||||
except Exception as e: | |||||
if e.__class__ in AllowableExceptions: | |||||
return | |||||
raise_exception(e, lvars['TARGETS'], s) | |||||
else: | |||||
if key in lvars: | |||||
s = lvars[key] | |||||
elif key in self.gvars: | |||||
s = self.gvars[key] | |||||
elif not NameError in AllowableExceptions: | |||||
raise_exception(NameError(), lvars['TARGETS'], s) | |||||
else: | |||||
return | |||||
# Before re-expanding the result, handle | |||||
# recursive expansion by copying the local | |||||
# variable dictionary and overwriting a null | |||||
# string for the value of the variable name | |||||
# we just expanded. | |||||
lv = lvars.copy() | |||||
var = key.split('.')[0] | |||||
lv[var] = '' | |||||
self.substitute(s, lv, 0) | |||||
self.this_word() | |||||
elif is_Sequence(s): | |||||
for a in s: | |||||
self.substitute(a, lvars, 1) | |||||
self.next_word() | |||||
elif callable(s): | |||||
try: | |||||
s = s(target=lvars['TARGETS'], | |||||
source=lvars['SOURCES'], | |||||
env=self.env, | |||||
for_signature=(self.mode != SUBST_CMD)) | |||||
except TypeError: | |||||
# This probably indicates that it's a callable | |||||
# object that doesn't match our calling arguments | |||||
# (like an Action). | |||||
if self.mode == SUBST_RAW: | |||||
self.append(s) | |||||
return | |||||
s = self.conv(s) | |||||
self.substitute(s, lvars, within_list) | |||||
elif s is None: | |||||
self.this_word() | |||||
else: | |||||
self.append(s) | |||||
def substitute(self, args, lvars, within_list): | |||||
"""Substitute expansions in an argument or list of arguments. | |||||
This serves as a wrapper for splitting up a string into | |||||
separate tokens. | |||||
""" | |||||
if is_String(args) and not isinstance(args, CmdStringHolder): | |||||
args = str(args) # In case it's a UserString. | |||||
args = _separate_args.findall(args) | |||||
for a in args: | |||||
if a[0] in ' \t\n\r\f\v': | |||||
if '\n' in a: | |||||
self.next_line() | |||||
elif within_list: | |||||
self.append(a) | |||||
else: | |||||
self.next_word() | |||||
else: | |||||
self.expand(a, lvars, within_list) | |||||
else: | |||||
self.expand(args, lvars, within_list) | |||||
def next_line(self): | |||||
"""Arrange for the next word to start a new line. This | |||||
is like starting a new word, except that we have to append | |||||
another line to the result.""" | |||||
collections.UserList.append(self, []) | |||||
self.next_word() | |||||
def this_word(self): | |||||
"""Arrange for the next word to append to the end of the | |||||
current last word in the result.""" | |||||
self.append = self.add_to_current_word | |||||
def next_word(self): | |||||
"""Arrange for the next word to start a new word.""" | |||||
self.append = self.add_new_word | |||||
def add_to_current_word(self, x): | |||||
"""Append the string x to the end of the current last word | |||||
in the result. If that is not possible, then just add | |||||
it as a new word. Make sure the entire concatenated string | |||||
inherits the object attributes of x (in particular, the | |||||
escape function) by wrapping it as CmdStringHolder.""" | |||||
if not self.in_strip or self.mode != SUBST_SIG: | |||||
try: | |||||
current_word = self[-1][-1] | |||||
except IndexError: | |||||
self.add_new_word(x) | |||||
else: | |||||
# All right, this is a hack and it should probably | |||||
# be refactored out of existence in the future. | |||||
# The issue is that we want to smoosh words together | |||||
# and make one file name that gets escaped if | |||||
# we're expanding something like foo$EXTENSION, | |||||
# but we don't want to smoosh them together if | |||||
# it's something like >$TARGET, because then we'll | |||||
# treat the '>' like it's part of the file name. | |||||
# So for now, just hard-code looking for the special | |||||
# command-line redirection characters... | |||||
try: | |||||
last_char = str(current_word)[-1] | |||||
except IndexError: | |||||
last_char = '\0' | |||||
if last_char in '<>|': | |||||
self.add_new_word(x) | |||||
else: | |||||
y = current_word + x | |||||
# We used to treat a word appended to a literal | |||||
# as a literal itself, but this caused problems | |||||
# with interpreting quotes around space-separated | |||||
# targets on command lines. Removing this makes | |||||
# none of the "substantive" end-to-end tests fail, | |||||
# so we'll take this out but leave it commented | |||||
# for now in case there's a problem not covered | |||||
# by the test cases and we need to resurrect this. | |||||
#literal1 = self.literal(self[-1][-1]) | |||||
#literal2 = self.literal(x) | |||||
y = self.conv(y) | |||||
if is_String(y): | |||||
#y = CmdStringHolder(y, literal1 or literal2) | |||||
y = CmdStringHolder(y, None) | |||||
self[-1][-1] = y | |||||
def add_new_word(self, x): | |||||
if not self.in_strip or self.mode != SUBST_SIG: | |||||
literal = self.literal(x) | |||||
x = self.conv(x) | |||||
if is_String(x): | |||||
x = CmdStringHolder(x, literal) | |||||
self[-1].append(x) | |||||
self.append = self.add_to_current_word | |||||
def literal(self, x): | |||||
try: | |||||
l = x.is_literal | |||||
except AttributeError: | |||||
return None | |||||
else: | |||||
return l() | |||||
def open_strip(self, x): | |||||
"""Handle the "open strip" $( token.""" | |||||
self.add_strip(x) | |||||
self.in_strip = 1 | |||||
def close_strip(self, x): | |||||
"""Handle the "close strip" $) token.""" | |||||
self.add_strip(x) | |||||
self.in_strip = None | |||||
if conv is None: | |||||
conv = _strconv[mode] | |||||
# Doing this every time is a bit of a waste, since the Executor | |||||
# has typically already populated the OverrideEnvironment with | |||||
# $TARGET/$SOURCE variables. We're keeping this (for now), though, | |||||
# because it supports existing behavior that allows us to call | |||||
# an Action directly with an arbitrary target+source pair, which | |||||
# we use in Tool/tex.py to handle calling $BIBTEX when necessary. | |||||
# If we dropped that behavior (or found another way to cover it), | |||||
# we could get rid of this call completely and just rely on the | |||||
# Executor setting the variables. | |||||
if 'TARGET' not in lvars: | |||||
d = subst_dict(target, source) | |||||
if d: | |||||
lvars = lvars.copy() | |||||
lvars.update(d) | |||||
# We're (most likely) going to eval() things. If Python doesn't | |||||
# find a __builtins__ value in the global dictionary used for eval(), | |||||
# it copies the current global values for you. Avoid this by | |||||
# setting it explicitly and then deleting, so we don't pollute the | |||||
# construction environment Dictionary(ies) that are typically used | |||||
# for expansion. | |||||
gvars['__builtins__'] = __builtins__ | |||||
ls = ListSubber(env, mode, conv, gvars) | |||||
ls.substitute(strSubst, lvars, 0) | |||||
try: | |||||
del gvars['__builtins__'] | |||||
except KeyError: | |||||
pass | |||||
return ls.data | |||||
def scons_subst_once(strSubst, env, key): | |||||
"""Perform single (non-recursive) substitution of a single | |||||
construction variable keyword. | |||||
This is used when setting a variable when copying or overriding values | |||||
in an Environment. We want to capture (expand) the old value before | |||||
we override it, so people can do things like: | |||||
env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') | |||||
We do this with some straightforward, brute-force code here... | |||||
""" | |||||
if isinstance(strSubst, str) and strSubst.find('$') < 0: | |||||
return strSubst | |||||
matchlist = ['$' + key, '${' + key + '}'] | |||||
val = env.get(key, '') | |||||
def sub_match(match, val=val, matchlist=matchlist): | |||||
a = match.group(1) | |||||
if a in matchlist: | |||||
a = val | |||||
if is_Sequence(a): | |||||
return ' '.join(map(str, a)) | |||||
else: | |||||
return str(a) | |||||
if is_Sequence(strSubst): | |||||
result = [] | |||||
for arg in strSubst: | |||||
if is_String(arg): | |||||
if arg in matchlist: | |||||
arg = val | |||||
if is_Sequence(arg): | |||||
result.extend(arg) | |||||
else: | |||||
result.append(arg) | |||||
else: | |||||
result.append(_dollar_exps.sub(sub_match, arg)) | |||||
else: | |||||
result.append(arg) | |||||
return result | |||||
elif is_String(strSubst): | |||||
return _dollar_exps.sub(sub_match, strSubst) | |||||
else: | |||||
return strSubst | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,61 @@ | |||||
"""SCons.Tool.386asm | |||||
Tool specification for the 386ASM assembler for the Phar Lap ETS embedded | |||||
operating system. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/386asm.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from SCons.Tool.PharLapCommon import addPharLapPaths | |||||
import SCons.Util | |||||
as_module = __import__('as', globals(), locals(), [], 1) | |||||
def generate(env): | |||||
"""Add Builders and construction variables for ar to an Environment.""" | |||||
as_module.generate(env) | |||||
env['AS'] = '386asm' | |||||
env['ASFLAGS'] = SCons.Util.CLVar('') | |||||
env['ASPPFLAGS'] = '$ASFLAGS' | |||||
env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET' | |||||
env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET' | |||||
addPharLapPaths(env) | |||||
def exists(env): | |||||
return env.Detect('386asm') | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,69 @@ | |||||
from __future__ import print_function | |||||
"""SCons.Tool.DCommon | |||||
Common code for the various D tools. | |||||
Coded by Russel Winder (russel@winder.org.uk) | |||||
2012-09-06 | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/DCommon.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os.path | |||||
def isD(env, source): | |||||
if not source: | |||||
return 0 | |||||
for s in source: | |||||
if s.sources: | |||||
ext = os.path.splitext(str(s.sources[0]))[1] | |||||
if ext == '.d': | |||||
return 1 | |||||
return 0 | |||||
def addDPATHToEnv(env, executable): | |||||
dPath = env.WhereIs(executable) | |||||
if dPath: | |||||
phobosDir = dPath[:dPath.rindex(executable)] + '/../src/phobos' | |||||
if os.path.isdir(phobosDir): | |||||
env.Append(DPATH=[phobosDir]) | |||||
def allAtOnceEmitter(target, source, env): | |||||
if env['DC'] in ('ldc2', 'dmd'): | |||||
env.SideEffect(str(target[0]) + '.o', target[0]) | |||||
env.Clean(target[0], str(target[0]) + '.o') | |||||
return target, source | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,280 @@ | |||||
"""SCons.Tool.FortranCommon | |||||
Stuff for processing Fortran, common to all fortran dialects. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
from __future__ import print_function | |||||
__revision__ = "src/engine/SCons/Tool/FortranCommon.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import re | |||||
import os.path | |||||
import SCons.Action | |||||
import SCons.Defaults | |||||
import SCons.Scanner.Fortran | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
def isfortran(env, source): | |||||
"""Return 1 if any of code in source has fortran files in it, 0 | |||||
otherwise.""" | |||||
try: | |||||
fsuffixes = env['FORTRANSUFFIXES'] | |||||
except KeyError: | |||||
# If no FORTRANSUFFIXES, no fortran tool, so there is no need to look | |||||
# for fortran sources. | |||||
return 0 | |||||
if not source: | |||||
# Source might be None for unusual cases like SConf. | |||||
return 0 | |||||
for s in source: | |||||
if s.sources: | |||||
ext = os.path.splitext(str(s.sources[0]))[1] | |||||
if ext in fsuffixes: | |||||
return 1 | |||||
return 0 | |||||
def _fortranEmitter(target, source, env): | |||||
node = source[0].rfile() | |||||
if not node.exists() and not node.is_derived(): | |||||
print("Could not locate " + str(node.name)) | |||||
return ([], []) | |||||
mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" | |||||
cre = re.compile(mod_regex,re.M) | |||||
# Retrieve all USE'd module names | |||||
modules = cre.findall(node.get_text_contents()) | |||||
# Remove unique items from the list | |||||
modules = SCons.Util.unique(modules) | |||||
# Convert module name to a .mod filename | |||||
suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source) | |||||
moddir = env.subst('$FORTRANMODDIR', target=target, source=source) | |||||
modules = [x.lower() + suffix for x in modules] | |||||
for m in modules: | |||||
target.append(env.fs.File(m, moddir)) | |||||
return (target, source) | |||||
def FortranEmitter(target, source, env): | |||||
target, source = _fortranEmitter(target, source, env) | |||||
return SCons.Defaults.StaticObjectEmitter(target, source, env) | |||||
def ShFortranEmitter(target, source, env): | |||||
target, source = _fortranEmitter(target, source, env) | |||||
return SCons.Defaults.SharedObjectEmitter(target, source, env) | |||||
def ComputeFortranSuffixes(suffixes, ppsuffixes): | |||||
"""suffixes are fortran source files, and ppsuffixes the ones to be | |||||
pre-processed. Both should be sequences, not strings.""" | |||||
assert len(suffixes) > 0 | |||||
s = suffixes[0] | |||||
sup = s.upper() | |||||
upper_suffixes = [_.upper() for _ in suffixes] | |||||
if SCons.Util.case_sensitive_suffixes(s, sup): | |||||
ppsuffixes.extend(upper_suffixes) | |||||
else: | |||||
suffixes.extend(upper_suffixes) | |||||
def CreateDialectActions(dialect): | |||||
"""Create dialect specific actions.""" | |||||
CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect) | |||||
CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect) | |||||
ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect) | |||||
ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect) | |||||
return CompAction, CompPPAction, ShCompAction, ShCompPPAction | |||||
def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0): | |||||
"""Add dialect specific construction variables.""" | |||||
ComputeFortranSuffixes(suffixes, ppsuffixes) | |||||
fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect) | |||||
for suffix in suffixes + ppsuffixes: | |||||
SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan) | |||||
env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes) | |||||
compaction, compppaction, shcompaction, shcompppaction = \ | |||||
CreateDialectActions(dialect) | |||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env) | |||||
for suffix in suffixes: | |||||
static_obj.add_action(suffix, compaction) | |||||
shared_obj.add_action(suffix, shcompaction) | |||||
static_obj.add_emitter(suffix, FortranEmitter) | |||||
shared_obj.add_emitter(suffix, ShFortranEmitter) | |||||
for suffix in ppsuffixes: | |||||
static_obj.add_action(suffix, compppaction) | |||||
shared_obj.add_action(suffix, shcompppaction) | |||||
static_obj.add_emitter(suffix, FortranEmitter) | |||||
shared_obj.add_emitter(suffix, ShFortranEmitter) | |||||
if '%sFLAGS' % dialect not in env: | |||||
env['%sFLAGS' % dialect] = SCons.Util.CLVar('') | |||||
if 'SH%sFLAGS' % dialect not in env: | |||||
env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) | |||||
# If a tool does not define fortran prefix/suffix for include path, use C ones | |||||
if 'INC%sPREFIX' % dialect not in env: | |||||
env['INC%sPREFIX' % dialect] = '$INCPREFIX' | |||||
if 'INC%sSUFFIX' % dialect not in env: | |||||
env['INC%sSUFFIX' % dialect] = '$INCSUFFIX' | |||||
env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect) | |||||
if support_module == 1: | |||||
env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) | |||||
env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) | |||||
env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) | |||||
env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) | |||||
else: | |||||
env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) | |||||
env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) | |||||
env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) | |||||
env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) | |||||
def add_fortran_to_env(env): | |||||
"""Add Builders and construction variables for Fortran to an Environment.""" | |||||
try: | |||||
FortranSuffixes = env['FORTRANFILESUFFIXES'] | |||||
except KeyError: | |||||
FortranSuffixes = ['.f', '.for', '.ftn'] | |||||
#print("Adding %s to fortran suffixes" % FortranSuffixes) | |||||
try: | |||||
FortranPPSuffixes = env['FORTRANPPFILESUFFIXES'] | |||||
except KeyError: | |||||
FortranPPSuffixes = ['.fpp', '.FPP'] | |||||
DialectAddToEnv(env, "FORTRAN", FortranSuffixes, | |||||
FortranPPSuffixes, support_module = 1) | |||||
env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX | |||||
env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX | |||||
env['FORTRANMODDIR'] = '' # where the compiler should place .mod files | |||||
env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX | |||||
env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX | |||||
env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' | |||||
def add_f77_to_env(env): | |||||
"""Add Builders and construction variables for f77 to an Environment.""" | |||||
try: | |||||
F77Suffixes = env['F77FILESUFFIXES'] | |||||
except KeyError: | |||||
F77Suffixes = ['.f77'] | |||||
#print("Adding %s to f77 suffixes" % F77Suffixes) | |||||
try: | |||||
F77PPSuffixes = env['F77PPFILESUFFIXES'] | |||||
except KeyError: | |||||
F77PPSuffixes = [] | |||||
DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes) | |||||
def add_f90_to_env(env): | |||||
"""Add Builders and construction variables for f90 to an Environment.""" | |||||
try: | |||||
F90Suffixes = env['F90FILESUFFIXES'] | |||||
except KeyError: | |||||
F90Suffixes = ['.f90'] | |||||
#print("Adding %s to f90 suffixes" % F90Suffixes) | |||||
try: | |||||
F90PPSuffixes = env['F90PPFILESUFFIXES'] | |||||
except KeyError: | |||||
F90PPSuffixes = [] | |||||
DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, | |||||
support_module = 1) | |||||
def add_f95_to_env(env): | |||||
"""Add Builders and construction variables for f95 to an Environment.""" | |||||
try: | |||||
F95Suffixes = env['F95FILESUFFIXES'] | |||||
except KeyError: | |||||
F95Suffixes = ['.f95'] | |||||
#print("Adding %s to f95 suffixes" % F95Suffixes) | |||||
try: | |||||
F95PPSuffixes = env['F95PPFILESUFFIXES'] | |||||
except KeyError: | |||||
F95PPSuffixes = [] | |||||
DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, | |||||
support_module = 1) | |||||
def add_f03_to_env(env): | |||||
"""Add Builders and construction variables for f03 to an Environment.""" | |||||
try: | |||||
F03Suffixes = env['F03FILESUFFIXES'] | |||||
except KeyError: | |||||
F03Suffixes = ['.f03'] | |||||
#print("Adding %s to f95 suffixes" % F95Suffixes) | |||||
try: | |||||
F03PPSuffixes = env['F03PPFILESUFFIXES'] | |||||
except KeyError: | |||||
F03PPSuffixes = [] | |||||
DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, | |||||
support_module = 1) | |||||
def add_f08_to_env(env): | |||||
"""Add Builders and construction variables for f08 to an Environment.""" | |||||
try: | |||||
F08Suffixes = env['F08FILESUFFIXES'] | |||||
except KeyError: | |||||
F08Suffixes = ['.f08'] | |||||
try: | |||||
F08PPSuffixes = env['F08PPFILESUFFIXES'] | |||||
except KeyError: | |||||
F08PPSuffixes = [] | |||||
DialectAddToEnv(env, "F08", F08Suffixes, F08PPSuffixes, | |||||
support_module = 1) | |||||
def add_all_to_env(env): | |||||
"""Add builders and construction variables for all supported fortran | |||||
dialects.""" | |||||
add_fortran_to_env(env) | |||||
add_f77_to_env(env) | |||||
add_f90_to_env(env) | |||||
add_f95_to_env(env) | |||||
add_f03_to_env(env) | |||||
add_f08_to_env(env) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,469 @@ | |||||
"""SCons.Tool.GettextCommon module | |||||
Used by several tools of `gettext` toolset. | |||||
""" | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Tool/GettextCommon.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Warnings | |||||
import re | |||||
############################################################################# | |||||
class XgettextToolWarning(SCons.Warnings.Warning): pass | |||||
class XgettextNotFound(XgettextToolWarning): pass | |||||
class MsginitToolWarning(SCons.Warnings.Warning): pass | |||||
class MsginitNotFound(MsginitToolWarning): pass | |||||
class MsgmergeToolWarning(SCons.Warnings.Warning): pass | |||||
class MsgmergeNotFound(MsgmergeToolWarning): pass | |||||
class MsgfmtToolWarning(SCons.Warnings.Warning): pass | |||||
class MsgfmtNotFound(MsgfmtToolWarning): pass | |||||
############################################################################# | |||||
SCons.Warnings.enableWarningClass(XgettextToolWarning) | |||||
SCons.Warnings.enableWarningClass(XgettextNotFound) | |||||
SCons.Warnings.enableWarningClass(MsginitToolWarning) | |||||
SCons.Warnings.enableWarningClass(MsginitNotFound) | |||||
SCons.Warnings.enableWarningClass(MsgmergeToolWarning) | |||||
SCons.Warnings.enableWarningClass(MsgmergeNotFound) | |||||
SCons.Warnings.enableWarningClass(MsgfmtToolWarning) | |||||
SCons.Warnings.enableWarningClass(MsgfmtNotFound) | |||||
############################################################################# | |||||
############################################################################# | |||||
class _POTargetFactory(object): | |||||
""" A factory of `PO` target files. | |||||
Factory defaults differ from these of `SCons.Node.FS.FS`. We set `precious` | |||||
(this is required by builders and actions gettext) and `noclean` flags by | |||||
default for all produced nodes. | |||||
""" | |||||
def __init__(self, env, nodefault=True, alias=None, precious=True | |||||
, noclean=True): | |||||
""" Object constructor. | |||||
**Arguments** | |||||
- *env* (`SCons.Environment.Environment`) | |||||
- *nodefault* (`boolean`) - if `True`, produced nodes will be ignored | |||||
from default target `'.'` | |||||
- *alias* (`string`) - if provided, produced nodes will be automatically | |||||
added to this alias, and alias will be set as `AlwaysBuild` | |||||
- *precious* (`boolean`) - if `True`, the produced nodes will be set as | |||||
`Precious`. | |||||
- *noclen* (`boolean`) - if `True`, the produced nodes will be excluded | |||||
from `Clean`. | |||||
""" | |||||
self.env = env | |||||
self.alias = alias | |||||
self.precious = precious | |||||
self.noclean = noclean | |||||
self.nodefault = nodefault | |||||
def _create_node(self, name, factory, directory=None, create=1): | |||||
""" Create node, and set it up to factory settings. """ | |||||
import SCons.Util | |||||
node = factory(name, directory, create) | |||||
node.set_noclean(self.noclean) | |||||
node.set_precious(self.precious) | |||||
if self.nodefault: | |||||
self.env.Ignore('.', node) | |||||
if self.alias: | |||||
self.env.AlwaysBuild(self.env.Alias(self.alias, node)) | |||||
return node | |||||
def Entry(self, name, directory=None, create=1): | |||||
""" Create `SCons.Node.FS.Entry` """ | |||||
return self._create_node(name, self.env.fs.Entry, directory, create) | |||||
def File(self, name, directory=None, create=1): | |||||
""" Create `SCons.Node.FS.File` """ | |||||
return self._create_node(name, self.env.fs.File, directory, create) | |||||
############################################################################# | |||||
############################################################################# | |||||
_re_comment = re.compile(r'(#[^\n\r]+)$', re.M) | |||||
_re_lang = re.compile(r'([a-zA-Z0-9_]+)', re.M) | |||||
############################################################################# | |||||
def _read_linguas_from_files(env, linguas_files=None): | |||||
""" Parse `LINGUAS` file and return list of extracted languages """ | |||||
import SCons.Util | |||||
import SCons.Environment | |||||
global _re_comment | |||||
global _re_lang | |||||
if not SCons.Util.is_List(linguas_files) \ | |||||
and not SCons.Util.is_String(linguas_files) \ | |||||
and not isinstance(linguas_files, SCons.Node.FS.Base) \ | |||||
and linguas_files: | |||||
# If, linguas_files==True or such, then read 'LINGUAS' file. | |||||
linguas_files = ['LINGUAS'] | |||||
if linguas_files is None: | |||||
return [] | |||||
fnodes = env.arg2nodes(linguas_files) | |||||
linguas = [] | |||||
for fnode in fnodes: | |||||
contents = _re_comment.sub("", fnode.get_text_contents()) | |||||
ls = [l for l in _re_lang.findall(contents) if l] | |||||
linguas.extend(ls) | |||||
return linguas | |||||
############################################################################# | |||||
############################################################################# | |||||
from SCons.Builder import BuilderBase | |||||
############################################################################# | |||||
class _POFileBuilder(BuilderBase): | |||||
""" `PO` file builder. | |||||
This is multi-target single-source builder. In typical situation the source | |||||
is single `POT` file, e.g. `messages.pot`, and there are multiple `PO` | |||||
targets to be updated from this `POT`. We must run | |||||
`SCons.Builder.BuilderBase._execute()` separatelly for each target to track | |||||
dependencies separatelly for each target file. | |||||
**NOTE**: if we call `SCons.Builder.BuilderBase._execute(.., target, ...)` | |||||
with target being list of all targets, all targets would be rebuilt each time | |||||
one of the targets from this list is missing. This would happen, for example, | |||||
when new language `ll` enters `LINGUAS_FILE` (at this moment there is no | |||||
`ll.po` file yet). To avoid this, we override | |||||
`SCons.Builder.BuilerBase._execute()` and call it separatelly for each | |||||
target. Here we also append to the target list the languages read from | |||||
`LINGUAS_FILE`. | |||||
""" | |||||
# | |||||
# * The argument for overriding _execute(): We must use environment with | |||||
# builder overrides applied (see BuilderBase.__init__(). Here it comes for | |||||
# free. | |||||
# * The argument against using 'emitter': The emitter is called too late | |||||
# by BuilderBase._execute(). If user calls, for example: | |||||
# | |||||
# env.POUpdate(LINGUAS_FILE = 'LINGUAS') | |||||
# | |||||
# the builder throws error, because it is called with target=None, | |||||
# source=None and is trying to "generate" sources or target list first. | |||||
# If user calls | |||||
# | |||||
# env.POUpdate(['foo', 'baz'], LINGUAS_FILE = 'LINGUAS') | |||||
# | |||||
# the env.BuilderWrapper() calls our builder with target=None, | |||||
# source=['foo', 'baz']. The BuilderBase._execute() then splits execution | |||||
# and execute iterativelly (recursion) self._execute(None, source[i]). | |||||
# After that it calls emitter (which is quite too late). The emitter is | |||||
# also called in each iteration, what makes things yet worse. | |||||
def __init__(self, env, **kw): | |||||
if not 'suffix' in kw: | |||||
kw['suffix'] = '$POSUFFIX' | |||||
if not 'src_suffix' in kw: | |||||
kw['src_suffix'] = '$POTSUFFIX' | |||||
if not 'src_builder' in kw: | |||||
kw['src_builder'] = '_POTUpdateBuilder' | |||||
if not 'single_source' in kw: | |||||
kw['single_source'] = True | |||||
alias = None | |||||
if 'target_alias' in kw: | |||||
alias = kw['target_alias'] | |||||
del kw['target_alias'] | |||||
if not 'target_factory' in kw: | |||||
kw['target_factory'] = _POTargetFactory(env, alias=alias).File | |||||
BuilderBase.__init__(self, **kw) | |||||
def _execute(self, env, target, source, *args, **kw): | |||||
""" Execute builder's actions. | |||||
Here we append to `target` the languages read from `$LINGUAS_FILE` and | |||||
apply `SCons.Builder.BuilderBase._execute()` separatelly to each target. | |||||
The arguments and return value are same as for | |||||
`SCons.Builder.BuilderBase._execute()`. | |||||
""" | |||||
import SCons.Util | |||||
import SCons.Node | |||||
linguas_files = None | |||||
if 'LINGUAS_FILE' in env and env['LINGUAS_FILE']: | |||||
linguas_files = env['LINGUAS_FILE'] | |||||
# This prevents endless recursion loop (we'll be invoked once for | |||||
# each target appended here, we must not extend the list again). | |||||
env['LINGUAS_FILE'] = None | |||||
linguas = _read_linguas_from_files(env, linguas_files) | |||||
if SCons.Util.is_List(target): | |||||
target.extend(linguas) | |||||
elif target is not None: | |||||
target = [target] + linguas | |||||
else: | |||||
target = linguas | |||||
if not target: | |||||
# Let the SCons.BuilderBase to handle this patologic situation | |||||
return BuilderBase._execute(self, env, target, source, *args, **kw) | |||||
# The rest is ours | |||||
if not SCons.Util.is_List(target): | |||||
target = [target] | |||||
result = [] | |||||
for tgt in target: | |||||
r = BuilderBase._execute(self, env, [tgt], source, *args, **kw) | |||||
result.extend(r) | |||||
if linguas_files is not None: | |||||
env['LINGUAS_FILE'] = linguas_files | |||||
return SCons.Node.NodeList(result) | |||||
############################################################################# | |||||
import SCons.Environment | |||||
############################################################################# | |||||
def _translate(env, target=None, source=SCons.Environment._null, *args, **kw): | |||||
""" Function for `Translate()` pseudo-builder """ | |||||
if target is None: target = [] | |||||
pot = env.POTUpdate(None, source, *args, **kw) | |||||
po = env.POUpdate(target, pot, *args, **kw) | |||||
return po | |||||
############################################################################# | |||||
############################################################################# | |||||
class RPaths(object): | |||||
""" Callable object, which returns pathnames relative to SCons current | |||||
working directory. | |||||
It seems like `SCons.Node.FS.Base.get_path()` returns absolute paths | |||||
for nodes that are outside of current working directory (`env.fs.getcwd()`). | |||||
Here, we often have `SConscript`, `POT` and `PO` files within `po/` | |||||
directory and source files (e.g. `*.c`) outside of it. When generating `POT` | |||||
template file, references to source files are written to `POT` template, so | |||||
a translator may later quickly jump to appropriate source file and line from | |||||
its `PO` editor (e.g. `poedit`). Relative paths in `PO` file are usually | |||||
interpreted by `PO` editor as paths relative to the place, where `PO` file | |||||
lives. The absolute paths would make resultant `POT` file nonportable, as | |||||
the references would be correct only on the machine, where `POT` file was | |||||
recently re-created. For such reason, we need a function, which always | |||||
returns relative paths. This is the purpose of `RPaths` callable object. | |||||
The `__call__` method returns paths relative to current working directory, but | |||||
we assume, that *xgettext(1)* is run from the directory, where target file is | |||||
going to be created. | |||||
Note, that this may not work for files distributed over several hosts or | |||||
across different drives on windows. We assume here, that single local | |||||
filesystem holds both source files and target `POT` templates. | |||||
Intended use of `RPaths` - in `xgettext.py`:: | |||||
def generate(env): | |||||
from GettextCommon import RPaths | |||||
... | |||||
sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET, SOURCES)} $)' | |||||
env.Append( | |||||
... | |||||
XGETTEXTCOM = 'XGETTEXT ... ' + sources, | |||||
... | |||||
XgettextRPaths = RPaths(env) | |||||
) | |||||
""" | |||||
# NOTE: This callable object returns pathnames of dirs/files relative to | |||||
# current working directory. The pathname remains relative also for entries | |||||
# that are outside of current working directory (node, that | |||||
# SCons.Node.FS.File and siblings return absolute path in such case). For | |||||
# simplicity we compute path relative to current working directory, this | |||||
# seems be enough for our purposes (don't need TARGET variable and | |||||
# SCons.Defaults.Variable_Caller stuff). | |||||
def __init__(self, env): | |||||
""" Initialize `RPaths` callable object. | |||||
**Arguments**: | |||||
- *env* - a `SCons.Environment.Environment` object, defines *current | |||||
working dir*. | |||||
""" | |||||
self.env = env | |||||
# FIXME: I'm not sure, how it should be implemented (what the *args are in | |||||
# general, what is **kw). | |||||
def __call__(self, nodes, *args, **kw): | |||||
""" Return nodes' paths (strings) relative to current working directory. | |||||
**Arguments**: | |||||
- *nodes* ([`SCons.Node.FS.Base`]) - list of nodes. | |||||
- *args* - currently unused. | |||||
- *kw* - currently unused. | |||||
**Returns**: | |||||
- Tuple of strings, which represent paths relative to current working | |||||
directory (for given environment). | |||||
""" | |||||
import os | |||||
import SCons.Node.FS | |||||
rpaths = () | |||||
cwd = self.env.fs.getcwd().get_abspath() | |||||
for node in nodes: | |||||
rpath = None | |||||
if isinstance(node, SCons.Node.FS.Base): | |||||
rpath = os.path.relpath(node.get_abspath(), cwd) | |||||
# FIXME: Other types possible here? | |||||
if rpath is not None: | |||||
rpaths += (rpath,) | |||||
return rpaths | |||||
############################################################################# | |||||
############################################################################# | |||||
def _init_po_files(target, source, env): | |||||
""" Action function for `POInit` builder. """ | |||||
nop = lambda target, source, env: 0 | |||||
if 'POAUTOINIT' in env: | |||||
autoinit = env['POAUTOINIT'] | |||||
else: | |||||
autoinit = False | |||||
# Well, if everything outside works well, this loop should do single | |||||
# iteration. Otherwise we are rebuilding all the targets even, if just | |||||
# one has changed (but is this our fault?). | |||||
for tgt in target: | |||||
if not tgt.exists(): | |||||
if autoinit: | |||||
action = SCons.Action.Action('$MSGINITCOM', '$MSGINITCOMSTR') | |||||
else: | |||||
msg = 'File ' + repr(str(tgt)) + ' does not exist. ' \ | |||||
+ 'If you are a translator, you can create it through: \n' \ | |||||
+ '$MSGINITCOM' | |||||
action = SCons.Action.Action(nop, msg) | |||||
status = action([tgt], source, env) | |||||
if status: return status | |||||
return 0 | |||||
############################################################################# | |||||
############################################################################# | |||||
def _detect_xgettext(env): | |||||
""" Detects *xgettext(1)* binary """ | |||||
if 'XGETTEXT' in env: | |||||
return env['XGETTEXT'] | |||||
xgettext = env.Detect('xgettext'); | |||||
if xgettext: | |||||
return xgettext | |||||
raise SCons.Errors.StopError(XgettextNotFound, "Could not detect xgettext") | |||||
return None | |||||
############################################################################# | |||||
def _xgettext_exists(env): | |||||
return _detect_xgettext(env) | |||||
############################################################################# | |||||
############################################################################# | |||||
def _detect_msginit(env): | |||||
""" Detects *msginit(1)* program. """ | |||||
if 'MSGINIT' in env: | |||||
return env['MSGINIT'] | |||||
msginit = env.Detect('msginit'); | |||||
if msginit: | |||||
return msginit | |||||
raise SCons.Errors.StopError(MsginitNotFound, "Could not detect msginit") | |||||
return None | |||||
############################################################################# | |||||
def _msginit_exists(env): | |||||
return _detect_msginit(env) | |||||
############################################################################# | |||||
############################################################################# | |||||
def _detect_msgmerge(env): | |||||
""" Detects *msgmerge(1)* program. """ | |||||
if 'MSGMERGE' in env: | |||||
return env['MSGMERGE'] | |||||
msgmerge = env.Detect('msgmerge'); | |||||
if msgmerge: | |||||
return msgmerge | |||||
raise SCons.Errors.StopError(MsgmergeNotFound, "Could not detect msgmerge") | |||||
return None | |||||
############################################################################# | |||||
def _msgmerge_exists(env): | |||||
return _detect_msgmerge(env) | |||||
############################################################################# | |||||
############################################################################# | |||||
def _detect_msgfmt(env): | |||||
""" Detects *msgmfmt(1)* program. """ | |||||
if 'MSGFMT' in env: | |||||
return env['MSGFMT'] | |||||
msgfmt = env.Detect('msgfmt'); | |||||
if msgfmt: | |||||
return msgfmt | |||||
raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") | |||||
return None | |||||
############################################################################# | |||||
def _msgfmt_exists(env): | |||||
return _detect_msgfmt(env) | |||||
############################################################################# | |||||
############################################################################# | |||||
def tool_list(platform, env): | |||||
""" List tools that shall be generated by top-level `gettext` tool """ | |||||
return ['xgettext', 'msginit', 'msgmerge', 'msgfmt'] | |||||
############################################################################# |
@ -0,0 +1,324 @@ | |||||
"""SCons.Tool.JavaCommon | |||||
Stuff for processing Java. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/JavaCommon.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import os.path | |||||
import re | |||||
java_parsing = 1 | |||||
default_java_version = '1.4' | |||||
if java_parsing: | |||||
# Parse Java files for class names. | |||||
# | |||||
# This is a really cool parser from Charles Crain | |||||
# that finds appropriate class names in Java source. | |||||
# A regular expression that will find, in a java file: | |||||
# newlines; | |||||
# double-backslashes; | |||||
# a single-line comment "//"; | |||||
# single or double quotes preceeded by a backslash; | |||||
# single quotes, double quotes, open or close braces, semi-colons, | |||||
# periods, open or close parentheses; | |||||
# floating-point numbers; | |||||
# any alphanumeric token (keyword, class name, specifier); | |||||
# any alphanumeric token surrounded by angle brackets (generics); | |||||
# the multi-line comment begin and end tokens /* and */; | |||||
# array declarations "[]". | |||||
_reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + | |||||
r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' + | |||||
r'/\*|\*/|\[\])') | |||||
class OuterState(object): | |||||
"""The initial state for parsing a Java file for classes, | |||||
interfaces, and anonymous inner classes.""" | |||||
def __init__(self, version=default_java_version): | |||||
if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', '1.7', | |||||
'1.8', '5', '6'): | |||||
msg = "Java version %s not supported" % version | |||||
raise NotImplementedError(msg) | |||||
self.version = version | |||||
self.listClasses = [] | |||||
self.listOutputs = [] | |||||
self.stackBrackets = [] | |||||
self.brackets = 0 | |||||
self.nextAnon = 1 | |||||
self.localClasses = [] | |||||
self.stackAnonClassBrackets = [] | |||||
self.anonStacksStack = [[0]] | |||||
self.package = None | |||||
def trace(self): | |||||
pass | |||||
def __getClassState(self): | |||||
try: | |||||
return self.classState | |||||
except AttributeError: | |||||
ret = ClassState(self) | |||||
self.classState = ret | |||||
return ret | |||||
def __getPackageState(self): | |||||
try: | |||||
return self.packageState | |||||
except AttributeError: | |||||
ret = PackageState(self) | |||||
self.packageState = ret | |||||
return ret | |||||
def __getAnonClassState(self): | |||||
try: | |||||
return self.anonState | |||||
except AttributeError: | |||||
self.outer_state = self | |||||
ret = SkipState(1, AnonClassState(self)) | |||||
self.anonState = ret | |||||
return ret | |||||
def __getSkipState(self): | |||||
try: | |||||
return self.skipState | |||||
except AttributeError: | |||||
ret = SkipState(1, self) | |||||
self.skipState = ret | |||||
return ret | |||||
def __getAnonStack(self): | |||||
return self.anonStacksStack[-1] | |||||
def openBracket(self): | |||||
self.brackets = self.brackets + 1 | |||||
def closeBracket(self): | |||||
self.brackets = self.brackets - 1 | |||||
if len(self.stackBrackets) and \ | |||||
self.brackets == self.stackBrackets[-1]: | |||||
self.listOutputs.append('$'.join(self.listClasses)) | |||||
self.localClasses.pop() | |||||
self.listClasses.pop() | |||||
self.anonStacksStack.pop() | |||||
self.stackBrackets.pop() | |||||
if len(self.stackAnonClassBrackets) and \ | |||||
self.brackets == self.stackAnonClassBrackets[-1]: | |||||
self.__getAnonStack().pop() | |||||
self.stackAnonClassBrackets.pop() | |||||
def parseToken(self, token): | |||||
if token[:2] == '//': | |||||
return IgnoreState('\n', self) | |||||
elif token == '/*': | |||||
return IgnoreState('*/', self) | |||||
elif token == '{': | |||||
self.openBracket() | |||||
elif token == '}': | |||||
self.closeBracket() | |||||
elif token in [ '"', "'" ]: | |||||
return IgnoreState(token, self) | |||||
elif token == "new": | |||||
# anonymous inner class | |||||
if len(self.listClasses) > 0: | |||||
return self.__getAnonClassState() | |||||
return self.__getSkipState() # Skip the class name | |||||
elif token in ['class', 'interface', 'enum']: | |||||
if len(self.listClasses) == 0: | |||||
self.nextAnon = 1 | |||||
self.stackBrackets.append(self.brackets) | |||||
return self.__getClassState() | |||||
elif token == 'package': | |||||
return self.__getPackageState() | |||||
elif token == '.': | |||||
# Skip the attribute, it might be named "class", in which | |||||
# case we don't want to treat the following token as | |||||
# an inner class name... | |||||
return self.__getSkipState() | |||||
return self | |||||
def addAnonClass(self): | |||||
"""Add an anonymous inner class""" | |||||
if self.version in ('1.1', '1.2', '1.3', '1.4'): | |||||
clazz = self.listClasses[0] | |||||
self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) | |||||
elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6'): | |||||
self.stackAnonClassBrackets.append(self.brackets) | |||||
className = [] | |||||
className.extend(self.listClasses) | |||||
self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 | |||||
for anon in self.__getAnonStack(): | |||||
className.append(str(anon)) | |||||
self.listOutputs.append('$'.join(className)) | |||||
self.nextAnon = self.nextAnon + 1 | |||||
self.__getAnonStack().append(0) | |||||
def setPackage(self, package): | |||||
self.package = package | |||||
class AnonClassState(object): | |||||
"""A state that looks for anonymous inner classes.""" | |||||
def __init__(self, old_state): | |||||
# outer_state is always an instance of OuterState | |||||
self.outer_state = old_state.outer_state | |||||
self.old_state = old_state | |||||
self.brace_level = 0 | |||||
def parseToken(self, token): | |||||
# This is an anonymous class if and only if the next | |||||
# non-whitespace token is a bracket. Everything between | |||||
# braces should be parsed as normal java code. | |||||
if token[:2] == '//': | |||||
return IgnoreState('\n', self) | |||||
elif token == '/*': | |||||
return IgnoreState('*/', self) | |||||
elif token == '\n': | |||||
return self | |||||
elif token[0] == '<' and token[-1] == '>': | |||||
return self | |||||
elif token == '(': | |||||
self.brace_level = self.brace_level + 1 | |||||
return self | |||||
if self.brace_level > 0: | |||||
if token == 'new': | |||||
# look further for anonymous inner class | |||||
return SkipState(1, AnonClassState(self)) | |||||
elif token in [ '"', "'" ]: | |||||
return IgnoreState(token, self) | |||||
elif token == ')': | |||||
self.brace_level = self.brace_level - 1 | |||||
return self | |||||
if token == '{': | |||||
self.outer_state.addAnonClass() | |||||
return self.old_state.parseToken(token) | |||||
class SkipState(object): | |||||
"""A state that will skip a specified number of tokens before | |||||
reverting to the previous state.""" | |||||
def __init__(self, tokens_to_skip, old_state): | |||||
self.tokens_to_skip = tokens_to_skip | |||||
self.old_state = old_state | |||||
def parseToken(self, token): | |||||
self.tokens_to_skip = self.tokens_to_skip - 1 | |||||
if self.tokens_to_skip < 1: | |||||
return self.old_state | |||||
return self | |||||
class ClassState(object): | |||||
"""A state we go into when we hit a class or interface keyword.""" | |||||
def __init__(self, outer_state): | |||||
# outer_state is always an instance of OuterState | |||||
self.outer_state = outer_state | |||||
def parseToken(self, token): | |||||
# the next non-whitespace token should be the name of the class | |||||
if token == '\n': | |||||
return self | |||||
# If that's an inner class which is declared in a method, it | |||||
# requires an index prepended to the class-name, e.g. | |||||
# 'Foo$1Inner' | |||||
# http://scons.tigris.org/issues/show_bug.cgi?id=2087 | |||||
if self.outer_state.localClasses and \ | |||||
self.outer_state.stackBrackets[-1] > \ | |||||
self.outer_state.stackBrackets[-2]+1: | |||||
locals = self.outer_state.localClasses[-1] | |||||
try: | |||||
idx = locals[token] | |||||
locals[token] = locals[token]+1 | |||||
except KeyError: | |||||
locals[token] = 1 | |||||
token = str(locals[token]) + token | |||||
self.outer_state.localClasses.append({}) | |||||
self.outer_state.listClasses.append(token) | |||||
self.outer_state.anonStacksStack.append([0]) | |||||
return self.outer_state | |||||
class IgnoreState(object): | |||||
"""A state that will ignore all tokens until it gets to a | |||||
specified token.""" | |||||
def __init__(self, ignore_until, old_state): | |||||
self.ignore_until = ignore_until | |||||
self.old_state = old_state | |||||
def parseToken(self, token): | |||||
if self.ignore_until == token: | |||||
return self.old_state | |||||
return self | |||||
class PackageState(object): | |||||
"""The state we enter when we encounter the package keyword. | |||||
We assume the next token will be the package name.""" | |||||
def __init__(self, outer_state): | |||||
# outer_state is always an instance of OuterState | |||||
self.outer_state = outer_state | |||||
def parseToken(self, token): | |||||
self.outer_state.setPackage(token) | |||||
return self.outer_state | |||||
def parse_java_file(fn, version=default_java_version): | |||||
return parse_java(open(fn, 'r').read(), version) | |||||
def parse_java(contents, version=default_java_version, trace=None): | |||||
"""Parse a .java file and return a double of package directory, | |||||
plus a list of .class files that compiling that .java file will | |||||
produce""" | |||||
package = None | |||||
initial = OuterState(version) | |||||
currstate = initial | |||||
for token in _reToken.findall(contents): | |||||
# The regex produces a bunch of groups, but only one will | |||||
# have anything in it. | |||||
currstate = currstate.parseToken(token) | |||||
if trace: trace(token, currstate) | |||||
if initial.package: | |||||
package = initial.package.replace('.', os.sep) | |||||
return (package, initial.listOutputs) | |||||
else: | |||||
# Don't actually parse Java files for class names. | |||||
# | |||||
# We might make this a configurable option in the future if | |||||
# Java-file parsing takes too long (although it shouldn't relative | |||||
# to how long the Java compiler itself seems to take...). | |||||
def parse_java_file(fn): | |||||
""" "Parse" a .java file. | |||||
This actually just splits the file name, so the assumption here | |||||
is that the file name matches the public class name, and that | |||||
the path to the file is the same as the package name. | |||||
""" | |||||
return os.path.split(file) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,57 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """ | |||||
Common functions for Microsoft Visual Studio and Visual C/C++. | |||||
""" | |||||
import copy | |||||
import os | |||||
import re | |||||
import subprocess | |||||
import SCons.Errors | |||||
import SCons.Platform.win32 | |||||
import SCons.Util | |||||
from SCons.Tool.MSCommon.sdk import mssdk_exists, \ | |||||
mssdk_setup_env | |||||
from SCons.Tool.MSCommon.vc import msvc_exists, \ | |||||
msvc_setup_env, \ | |||||
msvc_setup_env_once, \ | |||||
msvc_version_to_maj_min | |||||
from SCons.Tool.MSCommon.vs import get_default_version, \ | |||||
get_vs_by_version, \ | |||||
merge_default_version, \ | |||||
msvs_exists, \ | |||||
query_versions | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,67 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Module to define supported Windows chip architectures. | |||||
""" | |||||
import os | |||||
class ArchDefinition(object): | |||||
""" | |||||
A class for defining architecture-specific settings and logic. | |||||
""" | |||||
def __init__(self, arch, synonyms=[]): | |||||
self.arch = arch | |||||
self.synonyms = synonyms | |||||
SupportedArchitectureList = [ | |||||
ArchitectureDefinition( | |||||
'x86', | |||||
['i386', 'i486', 'i586', 'i686'], | |||||
), | |||||
ArchitectureDefinition( | |||||
'x86_64', | |||||
['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], | |||||
), | |||||
ArchitectureDefinition( | |||||
'ia64', | |||||
['IA64'], | |||||
), | |||||
ArchitectureDefinition( | |||||
'arm', | |||||
['ARM'], | |||||
), | |||||
] | |||||
SupportedArchitectureMap = {} | |||||
for a in SupportedArchitectureList: | |||||
SupportedArchitectureMap[a.arch] = a | |||||
for s in a.synonyms: | |||||
SupportedArchitectureMap[s] = a | |||||
@ -0,0 +1,247 @@ | |||||
""" | |||||
Common helper functions for working with the Microsoft tool chain. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
from __future__ import print_function | |||||
__revision__ = "src/engine/SCons/Tool/MSCommon/common.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import copy | |||||
import os | |||||
import subprocess | |||||
import re | |||||
import SCons.Util | |||||
LOGFILE = os.environ.get('SCONS_MSCOMMON_DEBUG') | |||||
if LOGFILE == '-': | |||||
def debug(message): | |||||
print(message) | |||||
elif LOGFILE: | |||||
try: | |||||
import logging | |||||
except ImportError: | |||||
debug = lambda message: open(LOGFILE, 'a').write(message + '\n') | |||||
else: | |||||
logging.basicConfig(filename=LOGFILE, level=logging.DEBUG) | |||||
debug = logging.debug | |||||
else: | |||||
debug = lambda x: None | |||||
_is_win64 = None | |||||
def is_win64(): | |||||
"""Return true if running on windows 64 bits. | |||||
Works whether python itself runs in 64 bits or 32 bits.""" | |||||
# Unfortunately, python does not provide a useful way to determine | |||||
# if the underlying Windows OS is 32-bit or 64-bit. Worse, whether | |||||
# the Python itself is 32-bit or 64-bit affects what it returns, | |||||
# so nothing in sys.* or os.* help. | |||||
# Apparently the best solution is to use env vars that Windows | |||||
# sets. If PROCESSOR_ARCHITECTURE is not x86, then the python | |||||
# process is running in 64 bit mode (on a 64-bit OS, 64-bit | |||||
# hardware, obviously). | |||||
# If this python is 32-bit but the OS is 64, Windows will set | |||||
# ProgramW6432 and PROCESSOR_ARCHITEW6432 to non-null. | |||||
# (Checking for HKLM\Software\Wow6432Node in the registry doesn't | |||||
# work, because some 32-bit installers create it.) | |||||
global _is_win64 | |||||
if _is_win64 is None: | |||||
# I structured these tests to make it easy to add new ones or | |||||
# add exceptions in the future, because this is a bit fragile. | |||||
_is_win64 = False | |||||
if os.environ.get('PROCESSOR_ARCHITECTURE', 'x86') != 'x86': | |||||
_is_win64 = True | |||||
if os.environ.get('PROCESSOR_ARCHITEW6432'): | |||||
_is_win64 = True | |||||
if os.environ.get('ProgramW6432'): | |||||
_is_win64 = True | |||||
return _is_win64 | |||||
def read_reg(value, hkroot=SCons.Util.HKEY_LOCAL_MACHINE): | |||||
return SCons.Util.RegGetValue(hkroot, value)[0] | |||||
def has_reg(value): | |||||
"""Return True if the given key exists in HKEY_LOCAL_MACHINE, False | |||||
otherwise.""" | |||||
try: | |||||
SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) | |||||
ret = True | |||||
except SCons.Util.WinError: | |||||
ret = False | |||||
return ret | |||||
# Functions for fetching environment variable settings from batch files. | |||||
def normalize_env(env, keys, force=False): | |||||
"""Given a dictionary representing a shell environment, add the variables | |||||
from os.environ needed for the processing of .bat files; the keys are | |||||
controlled by the keys argument. | |||||
It also makes sure the environment values are correctly encoded. | |||||
If force=True, then all of the key values that exist are copied | |||||
into the returned dictionary. If force=false, values are only | |||||
copied if the key does not already exist in the copied dictionary. | |||||
Note: the environment is copied.""" | |||||
normenv = {} | |||||
if env: | |||||
for k in list(env.keys()): | |||||
normenv[k] = copy.deepcopy(env[k]) | |||||
for k in keys: | |||||
if k in os.environ and (force or not k in normenv): | |||||
normenv[k] = os.environ[k] | |||||
# This shouldn't be necessary, since the default environment should include system32, | |||||
# but keep this here to be safe, since it's needed to find reg.exe which the MSVC | |||||
# bat scripts use. | |||||
sys32_dir = os.path.join(os.environ.get("SystemRoot", | |||||
os.environ.get("windir", r"C:\Windows\system32")), | |||||
"System32") | |||||
if sys32_dir not in normenv['PATH']: | |||||
normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_dir | |||||
# Without Wbem in PATH, vcvarsall.bat has a "'wmic' is not recognized" | |||||
# error starting with Visual Studio 2017, although the script still | |||||
# seems to work anyway. | |||||
sys32_wbem_dir = os.path.join(sys32_dir, 'Wbem') | |||||
if sys32_wbem_dir not in normenv['PATH']: | |||||
normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_wbem_dir | |||||
debug("PATH: %s"%normenv['PATH']) | |||||
return normenv | |||||
def get_output(vcbat, args = None, env = None): | |||||
"""Parse the output of given bat file, with given args.""" | |||||
if env is None: | |||||
# Create a blank environment, for use in launching the tools | |||||
env = SCons.Environment.Environment(tools=[]) | |||||
# TODO: This is a hard-coded list of the variables that (may) need | |||||
# to be imported from os.environ[] for v[sc]*vars*.bat file | |||||
# execution to work. This list should really be either directly | |||||
# controlled by vc.py, or else derived from the common_tools_var | |||||
# settings in vs.py. | |||||
vs_vc_vars = [ | |||||
'COMSPEC', | |||||
# VS100 and VS110: Still set, but modern MSVC setup scripts will | |||||
# discard these if registry has values. However Intel compiler setup | |||||
# script still requires these as of 2013/2014. | |||||
'VS140COMNTOOLS', | |||||
'VS120COMNTOOLS', | |||||
'VS110COMNTOOLS', | |||||
'VS100COMNTOOLS', | |||||
'VS90COMNTOOLS', | |||||
'VS80COMNTOOLS', | |||||
'VS71COMNTOOLS', | |||||
'VS70COMNTOOLS', | |||||
'VS60COMNTOOLS', | |||||
] | |||||
env['ENV'] = normalize_env(env['ENV'], vs_vc_vars, force=False) | |||||
if args: | |||||
debug("Calling '%s %s'" % (vcbat, args)) | |||||
popen = SCons.Action._subproc(env, | |||||
'"%s" %s & set' % (vcbat, args), | |||||
stdin='devnull', | |||||
stdout=subprocess.PIPE, | |||||
stderr=subprocess.PIPE) | |||||
else: | |||||
debug("Calling '%s'" % vcbat) | |||||
popen = SCons.Action._subproc(env, | |||||
'"%s" & set' % vcbat, | |||||
stdin='devnull', | |||||
stdout=subprocess.PIPE, | |||||
stderr=subprocess.PIPE) | |||||
# Use the .stdout and .stderr attributes directly because the | |||||
# .communicate() method uses the threading module on Windows | |||||
# and won't work under Pythons not built with threading. | |||||
stdout = popen.stdout.read() | |||||
stderr = popen.stderr.read() | |||||
# Extra debug logic, uncomment if necessary | |||||
# debug('get_output():stdout:%s'%stdout) | |||||
# debug('get_output():stderr:%s'%stderr) | |||||
if stderr: | |||||
# TODO: find something better to do with stderr; | |||||
# this at least prevents errors from getting swallowed. | |||||
import sys | |||||
sys.stderr.write(stderr) | |||||
if popen.wait() != 0: | |||||
raise IOError(stderr.decode("mbcs")) | |||||
output = stdout.decode("mbcs") | |||||
return output | |||||
def parse_output(output, keep=("INCLUDE", "LIB", "LIBPATH", "PATH")): | |||||
""" | |||||
Parse output from running visual c++/studios vcvarsall.bat and running set | |||||
To capture the values listed in keep | |||||
""" | |||||
# dkeep is a dict associating key: path_list, where key is one item from | |||||
# keep, and pat_list the associated list of paths | |||||
dkeep = dict([(i, []) for i in keep]) | |||||
# rdk will keep the regex to match the .bat file output line starts | |||||
rdk = {} | |||||
for i in keep: | |||||
rdk[i] = re.compile('%s=(.*)' % i, re.I) | |||||
def add_env(rmatch, key, dkeep=dkeep): | |||||
path_list = rmatch.group(1).split(os.pathsep) | |||||
for path in path_list: | |||||
# Do not add empty paths (when a var ends with ;) | |||||
if path: | |||||
# XXX: For some reason, VC98 .bat file adds "" around the PATH | |||||
# values, and it screws up the environment later, so we strip | |||||
# it. | |||||
path = path.strip('"') | |||||
dkeep[key].append(str(path)) | |||||
for line in output.splitlines(): | |||||
for k, value in rdk.items(): | |||||
match = value.match(line) | |||||
if match: | |||||
add_env(match, k) | |||||
return dkeep | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,83 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """ | |||||
""" | |||||
import os | |||||
import re | |||||
import SCons.Util | |||||
from .common import read_reg, debug | |||||
# Original value recorded by dcournapeau | |||||
_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\.NETFramework\InstallRoot' | |||||
# On SGK's system | |||||
_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\Microsoft SDKs\.NETFramework\v2.0\InstallationFolder' | |||||
def find_framework_root(): | |||||
# XXX: find it from environment (FrameworkDir) | |||||
try: | |||||
froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT) | |||||
debug("Found framework install root in registry: {}".format(froot)) | |||||
except SCons.Util.WinError as e: | |||||
debug("Could not read reg key {}".format(_FRAMEWORKDIR_HKEY_ROOT)) | |||||
return None | |||||
if not os.path.exists(froot): | |||||
debug("{} not found on fs".format(froot)) | |||||
return None | |||||
return froot | |||||
def query_versions(): | |||||
froot = find_framework_root() | |||||
if froot: | |||||
contents = os.listdir(froot) | |||||
l = re.compile('v[0-9]+.*') | |||||
versions = [e for e in contents if l.match(e)] | |||||
def versrt(a,b): | |||||
# since version numbers aren't really floats... | |||||
aa = a[1:] | |||||
bb = b[1:] | |||||
aal = aa.split('.') | |||||
bbl = bb.split('.') | |||||
# sequence comparison in python is lexicographical | |||||
# which is exactly what we want. | |||||
# Note we sort backwards so the highest version is first. | |||||
return cmp(bbl,aal) | |||||
versions.sort(versrt) | |||||
else: | |||||
versions = [] | |||||
return versions | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,403 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Module to detect the Platform/Windows SDK | |||||
PSDK 2003 R1 is the earliest version detected. | |||||
""" | |||||
import os | |||||
import SCons.Errors | |||||
import SCons.Util | |||||
from . import common | |||||
debug = common.debug | |||||
# SDK Checks. This is of course a mess as everything else on MS platforms. Here | |||||
# is what we do to detect the SDK: | |||||
# | |||||
# For Windows SDK >= 6.0: just look into the registry entries: | |||||
# HKLM\Software\Microsoft\Microsoft SDKs\Windows | |||||
# All the keys in there are the available versions. | |||||
# | |||||
# For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not | |||||
# seem to be any sane registry key, so the precise location is hardcoded. | |||||
# | |||||
# For versions below 2003R1, it seems the PSDK is included with Visual Studio? | |||||
# | |||||
# Also, per the following: | |||||
# http://benjamin.smedbergs.us/blog/tag/atl/ | |||||
# VC++ Professional comes with the SDK, VC++ Express does not. | |||||
# Location of the SDK (checked for 6.1 only) | |||||
_CURINSTALLED_SDK_HKEY_ROOT = \ | |||||
r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder" | |||||
class SDKDefinition(object): | |||||
""" | |||||
An abstract base class for trying to find installed SDK directories. | |||||
""" | |||||
def __init__(self, version, **kw): | |||||
self.version = version | |||||
self.__dict__.update(kw) | |||||
def find_sdk_dir(self): | |||||
"""Try to find the MS SDK from the registry. | |||||
Return None if failed or the directory does not exist. | |||||
""" | |||||
if not SCons.Util.can_read_reg: | |||||
debug('find_sdk_dir(): can not read registry') | |||||
return None | |||||
hkey = self.HKEY_FMT % self.hkey_data | |||||
debug('find_sdk_dir(): checking registry:{}'.format(hkey)) | |||||
try: | |||||
sdk_dir = common.read_reg(hkey) | |||||
except SCons.Util.WinError as e: | |||||
debug('find_sdk_dir(): no SDK registry key {}'.format(repr(hkey))) | |||||
return None | |||||
debug('find_sdk_dir(): Trying SDK Dir: {}'.format(sdk_dir)) | |||||
if not os.path.exists(sdk_dir): | |||||
debug('find_sdk_dir(): {} not on file system'.format(sdk_dir)) | |||||
return None | |||||
ftc = os.path.join(sdk_dir, self.sanity_check_file) | |||||
if not os.path.exists(ftc): | |||||
debug("find_sdk_dir(): sanity check {} not found".format(ftc)) | |||||
return None | |||||
return sdk_dir | |||||
def get_sdk_dir(self): | |||||
"""Return the MSSSDK given the version string.""" | |||||
try: | |||||
return self._sdk_dir | |||||
except AttributeError: | |||||
sdk_dir = self.find_sdk_dir() | |||||
self._sdk_dir = sdk_dir | |||||
return sdk_dir | |||||
def get_sdk_vc_script(self,host_arch, target_arch): | |||||
""" Return the script to initialize the VC compiler installed by SDK | |||||
""" | |||||
if (host_arch == 'amd64' and target_arch == 'x86'): | |||||
# No cross tools needed compiling 32 bits on 64 bit machine | |||||
host_arch=target_arch | |||||
arch_string=target_arch | |||||
if (host_arch != target_arch): | |||||
arch_string='%s_%s'%(host_arch,target_arch) | |||||
debug("sdk.py: get_sdk_vc_script():arch_string:%s host_arch:%s target_arch:%s"%(arch_string, | |||||
host_arch, | |||||
target_arch)) | |||||
file=self.vc_setup_scripts.get(arch_string,None) | |||||
debug("sdk.py: get_sdk_vc_script():file:%s"%file) | |||||
return file | |||||
class WindowsSDK(SDKDefinition): | |||||
""" | |||||
A subclass for trying to find installed Windows SDK directories. | |||||
""" | |||||
HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder' | |||||
def __init__(self, *args, **kw): | |||||
SDKDefinition.__init__(self, *args, **kw) | |||||
self.hkey_data = self.version | |||||
class PlatformSDK(SDKDefinition): | |||||
""" | |||||
A subclass for trying to find installed Platform SDK directories. | |||||
""" | |||||
HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir' | |||||
def __init__(self, *args, **kw): | |||||
SDKDefinition.__init__(self, *args, **kw) | |||||
self.hkey_data = self.uuid | |||||
# | |||||
# The list of VC initialization scripts installed by the SDK | |||||
# These should be tried if the vcvarsall.bat TARGET_ARCH fails | |||||
preSDK61VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', | |||||
'amd64' : r'bin\vcvarsamd64.bat', | |||||
'x86_amd64': r'bin\vcvarsx86_amd64.bat', | |||||
'x86_ia64' : r'bin\vcvarsx86_ia64.bat', | |||||
'ia64' : r'bin\vcvarsia64.bat'} | |||||
SDK61VCSetupScripts = {'x86' : r'bin\vcvars32.bat', | |||||
'amd64' : r'bin\amd64\vcvarsamd64.bat', | |||||
'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat', | |||||
'x86_ia64' : r'bin\x86_ia64\vcvarsx86_ia64.bat', | |||||
'ia64' : r'bin\ia64\vcvarsia64.bat'} | |||||
SDK70VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', | |||||
'amd64' : r'bin\vcvars64.bat', | |||||
'x86_amd64': r'bin\vcvarsx86_amd64.bat', | |||||
'x86_ia64' : r'bin\vcvarsx86_ia64.bat', | |||||
'ia64' : r'bin\vcvarsia64.bat'} | |||||
SDK100VCSetupScripts = {'x86' : r'bin\vcvars32.bat', | |||||
'amd64' : r'bin\vcvars64.bat', | |||||
'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat', | |||||
'x86_arm' : r'bin\x86_arm\vcvarsx86_arm.bat'} | |||||
# The list of support SDKs which we know how to detect. | |||||
# | |||||
# The first SDK found in the list is the one used by default if there | |||||
# are multiple SDKs installed. Barring good reasons to the contrary, | |||||
# this means we should list SDKs from most recent to oldest. | |||||
# | |||||
# If you update this list, update the documentation in Tool/mssdk.xml. | |||||
SupportedSDKList = [ | |||||
WindowsSDK('10.0', | |||||
sanity_check_file=r'bin\SetEnv.Cmd', | |||||
include_subdir='include', | |||||
lib_subdir={ | |||||
'x86' : ['lib'], | |||||
'x86_64' : [r'lib\x64'], | |||||
'ia64' : [r'lib\ia64'], | |||||
}, | |||||
vc_setup_scripts = SDK70VCSetupScripts, | |||||
), | |||||
WindowsSDK('7.1', | |||||
sanity_check_file=r'bin\SetEnv.Cmd', | |||||
include_subdir='include', | |||||
lib_subdir={ | |||||
'x86' : ['lib'], | |||||
'x86_64' : [r'lib\x64'], | |||||
'ia64' : [r'lib\ia64'], | |||||
}, | |||||
vc_setup_scripts = SDK70VCSetupScripts, | |||||
), | |||||
WindowsSDK('7.0A', | |||||
sanity_check_file=r'bin\SetEnv.Cmd', | |||||
include_subdir='include', | |||||
lib_subdir={ | |||||
'x86' : ['lib'], | |||||
'x86_64' : [r'lib\x64'], | |||||
'ia64' : [r'lib\ia64'], | |||||
}, | |||||
vc_setup_scripts = SDK70VCSetupScripts, | |||||
), | |||||
WindowsSDK('7.0', | |||||
sanity_check_file=r'bin\SetEnv.Cmd', | |||||
include_subdir='include', | |||||
lib_subdir={ | |||||
'x86' : ['lib'], | |||||
'x86_64' : [r'lib\x64'], | |||||
'ia64' : [r'lib\ia64'], | |||||
}, | |||||
vc_setup_scripts = SDK70VCSetupScripts, | |||||
), | |||||
WindowsSDK('6.1', | |||||
sanity_check_file=r'bin\SetEnv.Cmd', | |||||
include_subdir='include', | |||||
lib_subdir={ | |||||
'x86' : ['lib'], | |||||
'x86_64' : [r'lib\x64'], | |||||
'ia64' : [r'lib\ia64'], | |||||
}, | |||||
vc_setup_scripts = SDK61VCSetupScripts, | |||||
), | |||||
WindowsSDK('6.0A', | |||||
sanity_check_file=r'include\windows.h', | |||||
include_subdir='include', | |||||
lib_subdir={ | |||||
'x86' : ['lib'], | |||||
'x86_64' : [r'lib\x64'], | |||||
'ia64' : [r'lib\ia64'], | |||||
}, | |||||
vc_setup_scripts = preSDK61VCSetupScripts, | |||||
), | |||||
WindowsSDK('6.0', | |||||
sanity_check_file=r'bin\gacutil.exe', | |||||
include_subdir='include', | |||||
lib_subdir='lib', | |||||
vc_setup_scripts = preSDK61VCSetupScripts, | |||||
), | |||||
PlatformSDK('2003R2', | |||||
sanity_check_file=r'SetEnv.Cmd', | |||||
uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", | |||||
vc_setup_scripts = preSDK61VCSetupScripts, | |||||
), | |||||
PlatformSDK('2003R1', | |||||
sanity_check_file=r'SetEnv.Cmd', | |||||
uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3", | |||||
vc_setup_scripts = preSDK61VCSetupScripts, | |||||
), | |||||
] | |||||
SupportedSDKMap = {} | |||||
for sdk in SupportedSDKList: | |||||
SupportedSDKMap[sdk.version] = sdk | |||||
# Finding installed SDKs isn't cheap, because it goes not only to the | |||||
# registry but also to the disk to sanity-check that there is, in fact, | |||||
# an SDK installed there and that the registry entry isn't just stale. | |||||
# Find this information once, when requested, and cache it. | |||||
InstalledSDKList = None | |||||
InstalledSDKMap = None | |||||
def get_installed_sdks(): | |||||
global InstalledSDKList | |||||
global InstalledSDKMap | |||||
debug('sdk.py:get_installed_sdks()') | |||||
if InstalledSDKList is None: | |||||
InstalledSDKList = [] | |||||
InstalledSDKMap = {} | |||||
for sdk in SupportedSDKList: | |||||
debug('MSCommon/sdk.py: trying to find SDK %s' % sdk.version) | |||||
if sdk.get_sdk_dir(): | |||||
debug('MSCommon/sdk.py:found SDK %s' % sdk.version) | |||||
InstalledSDKList.append(sdk) | |||||
InstalledSDKMap[sdk.version] = sdk | |||||
return InstalledSDKList | |||||
# We may be asked to update multiple construction environments with | |||||
# SDK information. When doing this, we check on-disk for whether | |||||
# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk | |||||
# is expensive, cache results by directory. | |||||
SDKEnvironmentUpdates = {} | |||||
def set_sdk_by_directory(env, sdk_dir): | |||||
global SDKEnvironmentUpdates | |||||
debug('set_sdk_by_directory: Using dir:%s'%sdk_dir) | |||||
try: | |||||
env_tuple_list = SDKEnvironmentUpdates[sdk_dir] | |||||
except KeyError: | |||||
env_tuple_list = [] | |||||
SDKEnvironmentUpdates[sdk_dir] = env_tuple_list | |||||
include_path = os.path.join(sdk_dir, 'include') | |||||
mfc_path = os.path.join(include_path, 'mfc') | |||||
atl_path = os.path.join(include_path, 'atl') | |||||
if os.path.exists(mfc_path): | |||||
env_tuple_list.append(('INCLUDE', mfc_path)) | |||||
if os.path.exists(atl_path): | |||||
env_tuple_list.append(('INCLUDE', atl_path)) | |||||
env_tuple_list.append(('INCLUDE', include_path)) | |||||
env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) | |||||
env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) | |||||
env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) | |||||
for variable, directory in env_tuple_list: | |||||
env.PrependENVPath(variable, directory) | |||||
def get_sdk_by_version(mssdk): | |||||
if mssdk not in SupportedSDKMap: | |||||
raise SCons.Errors.UserError("SDK version {} is not supported".format(repr(mssdk))) | |||||
get_installed_sdks() | |||||
return InstalledSDKMap.get(mssdk) | |||||
def get_default_sdk(): | |||||
"""Set up the default Platform/Windows SDK.""" | |||||
get_installed_sdks() | |||||
if not InstalledSDKList: | |||||
return None | |||||
return InstalledSDKList[0] | |||||
def mssdk_setup_env(env): | |||||
debug('sdk.py:mssdk_setup_env()') | |||||
if 'MSSDK_DIR' in env: | |||||
sdk_dir = env['MSSDK_DIR'] | |||||
if sdk_dir is None: | |||||
return | |||||
sdk_dir = env.subst(sdk_dir) | |||||
debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:{}'.format(sdk_dir)) | |||||
elif 'MSSDK_VERSION' in env: | |||||
sdk_version = env['MSSDK_VERSION'] | |||||
if sdk_version is None: | |||||
msg = "SDK version is specified as None" | |||||
raise SCons.Errors.UserError(msg) | |||||
sdk_version = env.subst(sdk_version) | |||||
mssdk = get_sdk_by_version(sdk_version) | |||||
if mssdk is None: | |||||
msg = "SDK version %s is not installed" % sdk_version | |||||
raise SCons.Errors.UserError(msg) | |||||
sdk_dir = mssdk.get_sdk_dir() | |||||
debug('sdk.py:mssdk_setup_env: Using MSSDK_VERSION:%s'%sdk_dir) | |||||
elif 'MSVS_VERSION' in env: | |||||
msvs_version = env['MSVS_VERSION'] | |||||
debug('sdk.py:mssdk_setup_env:Getting MSVS_VERSION from env:%s'%msvs_version) | |||||
if msvs_version is None: | |||||
debug('sdk.py:mssdk_setup_env thinks msvs_version is None') | |||||
return | |||||
msvs_version = env.subst(msvs_version) | |||||
from . import vs | |||||
msvs = vs.get_vs_by_version(msvs_version) | |||||
debug('sdk.py:mssdk_setup_env:msvs is :%s'%msvs) | |||||
if not msvs: | |||||
debug('sdk.py:mssdk_setup_env: no VS version detected, bailingout:%s'%msvs) | |||||
return | |||||
sdk_version = msvs.sdk_version | |||||
debug('sdk.py:msvs.sdk_version is %s'%sdk_version) | |||||
if not sdk_version: | |||||
return | |||||
mssdk = get_sdk_by_version(sdk_version) | |||||
if not mssdk: | |||||
mssdk = get_default_sdk() | |||||
if not mssdk: | |||||
return | |||||
sdk_dir = mssdk.get_sdk_dir() | |||||
debug('sdk.py:mssdk_setup_env: Using MSVS_VERSION:%s'%sdk_dir) | |||||
else: | |||||
mssdk = get_default_sdk() | |||||
if not mssdk: | |||||
return | |||||
sdk_dir = mssdk.get_sdk_dir() | |||||
debug('sdk.py:mssdk_setup_env: not using any env values. sdk_dir:%s'%sdk_dir) | |||||
set_sdk_by_directory(env, sdk_dir) | |||||
#print "No MSVS_VERSION: this is likely to be a bug" | |||||
def mssdk_exists(version=None): | |||||
sdks = get_installed_sdks() | |||||
if version is None: | |||||
return len(sdks) > 0 | |||||
return version in sdks | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,573 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
# TODO: | |||||
# * supported arch for versions: for old versions of batch file without | |||||
# argument, giving bogus argument cannot be detected, so we have to hardcode | |||||
# this here | |||||
# * print warning when msvc version specified but not found | |||||
# * find out why warning do not print | |||||
# * test on 64 bits XP + VS 2005 (and VS 6 if possible) | |||||
# * SDK | |||||
# * Assembly | |||||
__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Module for Visual C/C++ detection and configuration. | |||||
""" | |||||
import SCons.compat | |||||
import SCons.Util | |||||
import subprocess | |||||
import os | |||||
import platform | |||||
from string import digits as string_digits | |||||
import SCons.Warnings | |||||
from . import common | |||||
debug = common.debug | |||||
from . import sdk | |||||
get_installed_sdks = sdk.get_installed_sdks | |||||
class VisualCException(Exception): | |||||
pass | |||||
class UnsupportedVersion(VisualCException): | |||||
pass | |||||
class UnsupportedArch(VisualCException): | |||||
pass | |||||
class MissingConfiguration(VisualCException): | |||||
pass | |||||
class NoVersionFound(VisualCException): | |||||
pass | |||||
class BatchFileExecutionError(VisualCException): | |||||
pass | |||||
# Dict to 'canonalize' the arch | |||||
_ARCH_TO_CANONICAL = { | |||||
"amd64" : "amd64", | |||||
"emt64" : "amd64", | |||||
"i386" : "x86", | |||||
"i486" : "x86", | |||||
"i586" : "x86", | |||||
"i686" : "x86", | |||||
"ia64" : "ia64", | |||||
"itanium" : "ia64", | |||||
"x86" : "x86", | |||||
"x86_64" : "amd64", | |||||
"x86_amd64" : "x86_amd64", # Cross compile to 64 bit from 32bits | |||||
} | |||||
# Given a (host, target) tuple, return the argument for the bat file. Both host | |||||
# and targets should be canonalized. | |||||
_HOST_TARGET_ARCH_TO_BAT_ARCH = { | |||||
("x86", "x86"): "x86", | |||||
("x86", "amd64"): "x86_amd64", | |||||
("x86", "x86_amd64"): "x86_amd64", | |||||
("amd64", "x86_amd64"): "x86_amd64", # This is present in (at least) VS2012 express | |||||
("amd64", "amd64"): "amd64", | |||||
("amd64", "x86"): "x86", | |||||
("x86", "ia64"): "x86_ia64" | |||||
} | |||||
def get_host_target(env): | |||||
debug('vc.py:get_host_target()') | |||||
host_platform = env.get('HOST_ARCH') | |||||
if not host_platform: | |||||
host_platform = platform.machine() | |||||
# TODO(2.5): the native Python platform.machine() function returns | |||||
# '' on all Python versions before 2.6, after which it also uses | |||||
# PROCESSOR_ARCHITECTURE. | |||||
if not host_platform: | |||||
host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') | |||||
# Retain user requested TARGET_ARCH | |||||
req_target_platform = env.get('TARGET_ARCH') | |||||
debug('vc.py:get_host_target() req_target_platform:%s'%req_target_platform) | |||||
if req_target_platform: | |||||
# If user requested a specific platform then only try that one. | |||||
target_platform = req_target_platform | |||||
else: | |||||
target_platform = host_platform | |||||
try: | |||||
host = _ARCH_TO_CANONICAL[host_platform.lower()] | |||||
except KeyError as e: | |||||
msg = "Unrecognized host architecture %s" | |||||
raise ValueError(msg % repr(host_platform)) | |||||
try: | |||||
target = _ARCH_TO_CANONICAL[target_platform.lower()] | |||||
except KeyError as e: | |||||
all_archs = str(list(_ARCH_TO_CANONICAL.keys())) | |||||
raise ValueError("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) | |||||
return (host, target,req_target_platform) | |||||
# If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the | |||||
# MSVC_VERSION documentation in Tool/msvc.xml. | |||||
_VCVER = ["14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] | |||||
_VCVER_TO_PRODUCT_DIR = { | |||||
'14.1' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'')], # Visual Studio 2017 doesn't set this registry key anymore | |||||
'14.0' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], | |||||
'14.0Exp' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir')], | |||||
'12.0' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'), | |||||
], | |||||
'12.0Exp' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\12.0\Setup\VC\ProductDir'), | |||||
], | |||||
'11.0': [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'), | |||||
], | |||||
'11.0Exp' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'), | |||||
], | |||||
'10.0': [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'), | |||||
], | |||||
'10.0Exp' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'), | |||||
], | |||||
'9.0': [ | |||||
(SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',), | |||||
], | |||||
'9.0Exp' : [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'), | |||||
], | |||||
'8.0': [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'), | |||||
], | |||||
'8.0Exp': [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'), | |||||
], | |||||
'7.1': [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'), | |||||
], | |||||
'7.0': [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'), | |||||
], | |||||
'6.0': [ | |||||
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'), | |||||
] | |||||
} | |||||
def msvc_version_to_maj_min(msvc_version): | |||||
msvc_version_numeric = ''.join([x for x in msvc_version if x in string_digits + '.']) | |||||
t = msvc_version_numeric.split(".") | |||||
if not len(t) == 2: | |||||
raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) | |||||
try: | |||||
maj = int(t[0]) | |||||
min = int(t[1]) | |||||
return maj, min | |||||
except ValueError as e: | |||||
raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) | |||||
def is_host_target_supported(host_target, msvc_version): | |||||
"""Return True if the given (host, target) tuple is supported given the | |||||
msvc version. | |||||
Parameters | |||||
---------- | |||||
host_target: tuple | |||||
tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross | |||||
compilation from 32 bits windows to 64 bits. | |||||
msvc_version: str | |||||
msvc version (major.minor, e.g. 10.0) | |||||
Note | |||||
---- | |||||
This only check whether a given version *may* support the given (host, | |||||
target), not that the toolchain is actually present on the machine. | |||||
""" | |||||
# We assume that any Visual Studio version supports x86 as a target | |||||
if host_target[1] != "x86": | |||||
maj, min = msvc_version_to_maj_min(msvc_version) | |||||
if maj < 8: | |||||
return False | |||||
return True | |||||
def find_vc_pdir_vswhere(msvc_version): | |||||
""" | |||||
Find the MSVC product directory using vswhere.exe . | |||||
Run it asking for specified version and get MSVS install location | |||||
:param msvc_version: | |||||
:return: MSVC install dir | |||||
""" | |||||
vswhere_path = os.path.join( | |||||
'C:\\', | |||||
'Program Files (x86)', | |||||
'Microsoft Visual Studio', | |||||
'Installer', | |||||
'vswhere.exe' | |||||
) | |||||
vswhere_cmd = [vswhere_path, '-version', msvc_version, '-property', 'installationPath'] | |||||
if os.path.exists(vswhere_path): | |||||
sp = subprocess.Popen(vswhere_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |||||
vsdir, err = sp.communicate() | |||||
vsdir = vsdir.decode("mbcs") | |||||
vsdir = vsdir.rstrip() | |||||
vc_pdir = os.path.join(vsdir, 'VC') | |||||
return vc_pdir | |||||
else: | |||||
# No vswhere on system, no install info available | |||||
return None | |||||
def find_vc_pdir(msvc_version): | |||||
"""Try to find the product directory for the given | |||||
version. | |||||
Note | |||||
---- | |||||
If for some reason the requested version could not be found, an | |||||
exception which inherits from VisualCException will be raised.""" | |||||
root = 'Software\\' | |||||
try: | |||||
hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] | |||||
except KeyError: | |||||
debug("Unknown version of MSVC: %s" % msvc_version) | |||||
raise UnsupportedVersion("Unknown version %s" % msvc_version) | |||||
for hkroot, key in hkeys: | |||||
try: | |||||
comps = None | |||||
if not key: | |||||
comps = find_vc_pdir_vswhere(msvc_version) | |||||
if not comps: | |||||
debug('find_vc_dir(): no VC found via vswhere for version {}'.format(repr(key))) | |||||
raise SCons.Util.WinError | |||||
else: | |||||
if common.is_win64(): | |||||
try: | |||||
# ordinally at win64, try Wow6432Node first. | |||||
comps = common.read_reg(root + 'Wow6432Node\\' + key, hkroot) | |||||
except SCons.Util.WinError as e: | |||||
# at Microsoft Visual Studio for Python 2.7, value is not in Wow6432Node | |||||
pass | |||||
if not comps: | |||||
# not Win64, or Microsoft Visual Studio for Python 2.7 | |||||
comps = common.read_reg(root + key, hkroot) | |||||
except SCons.Util.WinError as e: | |||||
debug('find_vc_dir(): no VC registry key {}'.format(repr(key))) | |||||
else: | |||||
debug('find_vc_dir(): found VC in registry: {}'.format(comps)) | |||||
if os.path.exists(comps): | |||||
return comps | |||||
else: | |||||
debug('find_vc_dir(): reg says dir is {}, but it does not exist. (ignoring)'.format(comps)) | |||||
raise MissingConfiguration("registry dir {} not found on the filesystem".format(comps)) | |||||
return None | |||||
def find_batch_file(env,msvc_version,host_arch,target_arch): | |||||
""" | |||||
Find the location of the batch script which should set up the compiler | |||||
for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress | |||||
""" | |||||
pdir = find_vc_pdir(msvc_version) | |||||
if pdir is None: | |||||
raise NoVersionFound("No version of Visual Studio found") | |||||
debug('vc.py: find_batch_file() pdir:{}'.format(pdir)) | |||||
# filter out e.g. "Exp" from the version name | |||||
msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."]) | |||||
vernum = float(msvc_ver_numeric) | |||||
if 7 <= vernum < 8: | |||||
pdir = os.path.join(pdir, os.pardir, "Common7", "Tools") | |||||
batfilename = os.path.join(pdir, "vsvars32.bat") | |||||
elif vernum < 7: | |||||
pdir = os.path.join(pdir, "Bin") | |||||
batfilename = os.path.join(pdir, "vcvars32.bat") | |||||
elif 8 <= vernum <= 14: | |||||
batfilename = os.path.join(pdir, "vcvarsall.bat") | |||||
else: # vernum >= 14.1 VS2017 and above | |||||
batfilename = os.path.join(pdir, "Auxiliary", "Build", "vcvarsall.bat") | |||||
if not os.path.exists(batfilename): | |||||
debug("Not found: %s" % batfilename) | |||||
batfilename = None | |||||
installed_sdks=get_installed_sdks() | |||||
for _sdk in installed_sdks: | |||||
sdk_bat_file = _sdk.get_sdk_vc_script(host_arch,target_arch) | |||||
if not sdk_bat_file: | |||||
debug("vc.py:find_batch_file() not found:%s"%_sdk) | |||||
else: | |||||
sdk_bat_file_path = os.path.join(pdir,sdk_bat_file) | |||||
if os.path.exists(sdk_bat_file_path): | |||||
debug('vc.py:find_batch_file() sdk_bat_file_path:%s'%sdk_bat_file_path) | |||||
return (batfilename,sdk_bat_file_path) | |||||
return (batfilename,None) | |||||
__INSTALLED_VCS_RUN = None | |||||
def cached_get_installed_vcs(): | |||||
global __INSTALLED_VCS_RUN | |||||
if __INSTALLED_VCS_RUN is None: | |||||
ret = get_installed_vcs() | |||||
__INSTALLED_VCS_RUN = ret | |||||
return __INSTALLED_VCS_RUN | |||||
def get_installed_vcs(): | |||||
installed_versions = [] | |||||
for ver in _VCVER: | |||||
debug('trying to find VC %s' % ver) | |||||
try: | |||||
if find_vc_pdir(ver): | |||||
debug('found VC %s' % ver) | |||||
installed_versions.append(ver) | |||||
else: | |||||
debug('find_vc_pdir return None for ver %s' % ver) | |||||
except VisualCException as e: | |||||
debug('did not find VC %s: caught exception %s' % (ver, str(e))) | |||||
return installed_versions | |||||
def reset_installed_vcs(): | |||||
"""Make it try again to find VC. This is just for the tests.""" | |||||
__INSTALLED_VCS_RUN = None | |||||
# Running these batch files isn't cheap: most of the time spent in | |||||
# msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'" | |||||
# in multiple environments, for example: | |||||
# env1 = Environment(tools='msvs') | |||||
# env2 = Environment(tools='msvs') | |||||
# we can greatly improve the speed of the second and subsequent Environment | |||||
# (or Clone) calls by memoizing the environment variables set by vcvars*.bat. | |||||
script_env_stdout_cache = {} | |||||
def script_env(script, args=None): | |||||
cache_key = (script, args) | |||||
stdout = script_env_stdout_cache.get(cache_key, None) | |||||
if stdout is None: | |||||
stdout = common.get_output(script, args) | |||||
script_env_stdout_cache[cache_key] = stdout | |||||
# Stupid batch files do not set return code: we take a look at the | |||||
# beginning of the output for an error message instead | |||||
olines = stdout.splitlines() | |||||
if olines[0].startswith("The specified configuration type is missing"): | |||||
raise BatchFileExecutionError("\n".join(olines[:2])) | |||||
return common.parse_output(stdout) | |||||
def get_default_version(env): | |||||
debug('get_default_version()') | |||||
msvc_version = env.get('MSVC_VERSION') | |||||
msvs_version = env.get('MSVS_VERSION') | |||||
debug('get_default_version(): msvc_version:%s msvs_version:%s'%(msvc_version,msvs_version)) | |||||
if msvs_version and not msvc_version: | |||||
SCons.Warnings.warn( | |||||
SCons.Warnings.DeprecatedWarning, | |||||
"MSVS_VERSION is deprecated: please use MSVC_VERSION instead ") | |||||
return msvs_version | |||||
elif msvc_version and msvs_version: | |||||
if not msvc_version == msvs_version: | |||||
SCons.Warnings.warn( | |||||
SCons.Warnings.VisualVersionMismatch, | |||||
"Requested msvc version (%s) and msvs version (%s) do " \ | |||||
"not match: please use MSVC_VERSION only to request a " \ | |||||
"visual studio version, MSVS_VERSION is deprecated" \ | |||||
% (msvc_version, msvs_version)) | |||||
return msvs_version | |||||
if not msvc_version: | |||||
installed_vcs = cached_get_installed_vcs() | |||||
debug('installed_vcs:%s' % installed_vcs) | |||||
if not installed_vcs: | |||||
#msg = 'No installed VCs' | |||||
#debug('msv %s\n' % repr(msg)) | |||||
#SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) | |||||
debug('msvc_setup_env: No installed VCs') | |||||
return None | |||||
msvc_version = installed_vcs[0] | |||||
debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version)) | |||||
return msvc_version | |||||
def msvc_setup_env_once(env): | |||||
try: | |||||
has_run = env["MSVC_SETUP_RUN"] | |||||
except KeyError: | |||||
has_run = False | |||||
if not has_run: | |||||
msvc_setup_env(env) | |||||
env["MSVC_SETUP_RUN"] = True | |||||
def msvc_find_valid_batch_script(env,version): | |||||
debug('vc.py:msvc_find_valid_batch_script()') | |||||
# Find the host platform, target platform, and if present the requested | |||||
# target platform | |||||
(host_platform, target_platform,req_target_platform) = get_host_target(env) | |||||
try_target_archs = [target_platform] | |||||
debug("msvs_find_valid_batch_script(): req_target_platform %s target_platform:%s"%(req_target_platform,target_platform)) | |||||
# VS2012 has a "cross compile" environment to build 64 bit | |||||
# with x86_amd64 as the argument to the batch setup script | |||||
if req_target_platform in ('amd64','x86_64'): | |||||
try_target_archs.append('x86_amd64') | |||||
elif not req_target_platform and target_platform in ['amd64','x86_64']: | |||||
# There may not be "native" amd64, but maybe "cross" x86_amd64 tools | |||||
try_target_archs.append('x86_amd64') | |||||
# If the user hasn't specifically requested a TARGET_ARCH, and | |||||
# The TARGET_ARCH is amd64 then also try 32 bits if there are no viable | |||||
# 64 bit tools installed | |||||
try_target_archs.append('x86') | |||||
debug("msvs_find_valid_batch_script(): host_platform: %s try_target_archs:%s"%(host_platform, try_target_archs)) | |||||
d = None | |||||
for tp in try_target_archs: | |||||
# Set to current arch. | |||||
env['TARGET_ARCH']=tp | |||||
debug("vc.py:msvc_find_valid_batch_script() trying target_platform:%s"%tp) | |||||
host_target = (host_platform, tp) | |||||
if not is_host_target_supported(host_target, version): | |||||
warn_msg = "host, target = %s not supported for MSVC version %s" % \ | |||||
(host_target, version) | |||||
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) | |||||
arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target] | |||||
# Get just version numbers | |||||
maj, min = msvc_version_to_maj_min(version) | |||||
# VS2015+ | |||||
if maj >= 14: | |||||
if env.get('MSVC_UWP_APP') == '1': | |||||
# Initialize environment variables with store/universal paths | |||||
arg += ' store' | |||||
# Try to locate a batch file for this host/target platform combo | |||||
try: | |||||
(vc_script,sdk_script) = find_batch_file(env,version,host_platform,tp) | |||||
debug('vc.py:msvc_find_valid_batch_script() vc_script:%s sdk_script:%s'%(vc_script,sdk_script)) | |||||
except VisualCException as e: | |||||
msg = str(e) | |||||
debug('Caught exception while looking for batch file (%s)' % msg) | |||||
warn_msg = "VC version %s not installed. " + \ | |||||
"C/C++ compilers are most likely not set correctly.\n" + \ | |||||
" Installed versions are: %s" | |||||
warn_msg = warn_msg % (version, cached_get_installed_vcs()) | |||||
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) | |||||
continue | |||||
# Try to use the located batch file for this host/target platform combo | |||||
debug('vc.py:msvc_find_valid_batch_script() use_script 2 %s, args:%s\n' % (repr(vc_script), arg)) | |||||
if vc_script: | |||||
try: | |||||
d = script_env(vc_script, args=arg) | |||||
except BatchFileExecutionError as e: | |||||
debug('vc.py:msvc_find_valid_batch_script() use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e)) | |||||
vc_script=None | |||||
continue | |||||
if not vc_script and sdk_script: | |||||
debug('vc.py:msvc_find_valid_batch_script() use_script 4: trying sdk script: %s'%(sdk_script)) | |||||
try: | |||||
d = script_env(sdk_script) | |||||
except BatchFileExecutionError as e: | |||||
debug('vc.py:msvc_find_valid_batch_script() use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e)) | |||||
continue | |||||
elif not vc_script and not sdk_script: | |||||
debug('vc.py:msvc_find_valid_batch_script() use_script 6: Neither VC script nor SDK script found') | |||||
continue | |||||
debug("vc.py:msvc_find_valid_batch_script() Found a working script/target: %s %s"%(repr(sdk_script),arg)) | |||||
break # We've found a working target_platform, so stop looking | |||||
# If we cannot find a viable installed compiler, reset the TARGET_ARCH | |||||
# To it's initial value | |||||
if not d: | |||||
env['TARGET_ARCH']=req_target_platform | |||||
return d | |||||
def msvc_setup_env(env): | |||||
debug('msvc_setup_env()') | |||||
version = get_default_version(env) | |||||
if version is None: | |||||
warn_msg = "No version of Visual Studio compiler found - C/C++ " \ | |||||
"compilers most likely not set correctly" | |||||
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) | |||||
return None | |||||
debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version)) | |||||
# XXX: we set-up both MSVS version for backward | |||||
# compatibility with the msvs tool | |||||
env['MSVC_VERSION'] = version | |||||
env['MSVS_VERSION'] = version | |||||
env['MSVS'] = {} | |||||
use_script = env.get('MSVC_USE_SCRIPT', True) | |||||
if SCons.Util.is_String(use_script): | |||||
debug('vc.py:msvc_setup_env() use_script 1 %s\n' % repr(use_script)) | |||||
d = script_env(use_script) | |||||
elif use_script: | |||||
d = msvc_find_valid_batch_script(env,version) | |||||
debug('vc.py:msvc_setup_env() use_script 2 %s\n' % d) | |||||
if not d: | |||||
return d | |||||
else: | |||||
debug('MSVC_USE_SCRIPT set to False') | |||||
warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ | |||||
"set correctly." | |||||
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) | |||||
return None | |||||
for k, v in d.items(): | |||||
debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) | |||||
env.PrependENVPath(k, v, delete_existing=True) | |||||
def msvc_exists(version=None): | |||||
vcs = cached_get_installed_vcs() | |||||
if version is None: | |||||
return len(vcs) > 0 | |||||
return version in vcs |
@ -0,0 +1,573 @@ | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
__doc__ = """Module to detect Visual Studio and/or Visual C/C++ | |||||
""" | |||||
import os | |||||
import SCons.Errors | |||||
import SCons.Util | |||||
from .common import debug, \ | |||||
get_output, \ | |||||
is_win64, \ | |||||
normalize_env, \ | |||||
parse_output, \ | |||||
read_reg | |||||
import SCons.Tool.MSCommon.vc | |||||
class VisualStudio(object): | |||||
""" | |||||
An abstract base class for trying to find installed versions of | |||||
Visual Studio. | |||||
""" | |||||
def __init__(self, version, **kw): | |||||
self.version = version | |||||
kw['vc_version'] = kw.get('vc_version', version) | |||||
kw['sdk_version'] = kw.get('sdk_version', version) | |||||
self.__dict__.update(kw) | |||||
self._cache = {} | |||||
def find_batch_file(self): | |||||
vs_dir = self.get_vs_dir() | |||||
if not vs_dir: | |||||
debug('find_executable(): no vs_dir') | |||||
return None | |||||
batch_file = os.path.join(vs_dir, self.batch_file_path) | |||||
batch_file = os.path.normpath(batch_file) | |||||
if not os.path.isfile(batch_file): | |||||
debug('find_batch_file(): %s not on file system' % batch_file) | |||||
return None | |||||
return batch_file | |||||
def find_vs_dir_by_vc(self): | |||||
SCons.Tool.MSCommon.vc.get_installed_vcs() | |||||
dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version) | |||||
if not dir: | |||||
debug('find_vs_dir(): no installed VC %s' % self.vc_version) | |||||
return None | |||||
return dir | |||||
def find_vs_dir_by_reg(self): | |||||
root = 'Software\\' | |||||
if is_win64(): | |||||
root = root + 'Wow6432Node\\' | |||||
for key in self.hkeys: | |||||
if key=='use_dir': | |||||
return self.find_vs_dir_by_vc() | |||||
key = root + key | |||||
try: | |||||
comps = read_reg(key) | |||||
except SCons.Util.WinError as e: | |||||
debug('find_vs_dir_by_reg(): no VS registry key {}'.format(repr(key))) | |||||
else: | |||||
debug('find_vs_dir_by_reg(): found VS in registry: {}'.format(comps)) | |||||
return comps | |||||
return None | |||||
def find_vs_dir(self): | |||||
""" Can use registry or location of VC to find vs dir | |||||
First try to find by registry, and if that fails find via VC dir | |||||
""" | |||||
if True: | |||||
vs_dir=self.find_vs_dir_by_reg() | |||||
return vs_dir | |||||
else: | |||||
return self.find_vs_dir_by_vc() | |||||
def find_executable(self): | |||||
vs_dir = self.get_vs_dir() | |||||
if not vs_dir: | |||||
debug('find_executable(): no vs_dir ({})'.format(vs_dir)) | |||||
return None | |||||
executable = os.path.join(vs_dir, self.executable_path) | |||||
executable = os.path.normpath(executable) | |||||
if not os.path.isfile(executable): | |||||
debug('find_executable(): {} not on file system'.format(executable)) | |||||
return None | |||||
return executable | |||||
def get_batch_file(self): | |||||
try: | |||||
return self._cache['batch_file'] | |||||
except KeyError: | |||||
batch_file = self.find_batch_file() | |||||
self._cache['batch_file'] = batch_file | |||||
return batch_file | |||||
def get_executable(self): | |||||
try: | |||||
debug('get_executable using cache:%s'%self._cache['executable']) | |||||
return self._cache['executable'] | |||||
except KeyError: | |||||
executable = self.find_executable() | |||||
self._cache['executable'] = executable | |||||
debug('get_executable not in cache:%s'%executable) | |||||
return executable | |||||
def get_vs_dir(self): | |||||
try: | |||||
return self._cache['vs_dir'] | |||||
except KeyError: | |||||
vs_dir = self.find_vs_dir() | |||||
self._cache['vs_dir'] = vs_dir | |||||
return vs_dir | |||||
def get_supported_arch(self): | |||||
try: | |||||
return self._cache['supported_arch'] | |||||
except KeyError: | |||||
# RDEVE: for the time being use hardcoded lists | |||||
# supported_arch = self.find_supported_arch() | |||||
self._cache['supported_arch'] = self.supported_arch | |||||
return self.supported_arch | |||||
def reset(self): | |||||
self._cache = {} | |||||
# The list of supported Visual Studio versions we know how to detect. | |||||
# | |||||
# How to look for .bat file ? | |||||
# - VS 2008 Express (x86): | |||||
# * from registry key productdir, gives the full path to vsvarsall.bat. In | |||||
# HKEY_LOCAL_MACHINE): | |||||
# Software\Microsoft\VCEpress\9.0\Setup\VC\productdir | |||||
# * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC | |||||
# relatively to the path given by the variable. | |||||
# | |||||
# - VS 2008 Express (WoW6432: 32 bits on windows x64): | |||||
# Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir | |||||
# | |||||
# - VS 2005 Express (x86): | |||||
# * from registry key productdir, gives the full path to vsvarsall.bat. In | |||||
# HKEY_LOCAL_MACHINE): | |||||
# Software\Microsoft\VCEpress\8.0\Setup\VC\productdir | |||||
# * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC | |||||
# relatively to the path given by the variable. | |||||
# | |||||
# - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a | |||||
# productdir ? | |||||
# | |||||
# - VS 2003 .Net (pro edition ? x86): | |||||
# * from registry key productdir. The path is then ..\Common7\Tools\ | |||||
# relatively to the key. The key is in HKEY_LOCAL_MACHINE): | |||||
# Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir | |||||
# * from environmnent variable VS71COMNTOOLS: the path is the full path to | |||||
# vsvars32.bat | |||||
# | |||||
# - VS 98 (VS 6): | |||||
# * from registry key productdir. The path is then Bin | |||||
# relatively to the key. The key is in HKEY_LOCAL_MACHINE): | |||||
# Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir | |||||
# | |||||
# The first version found in the list is the one used by default if | |||||
# there are multiple versions installed. Barring good reasons to | |||||
# the contrary, this means we should list versions from most recent | |||||
# to oldest. Pro versions get listed before Express versions on the | |||||
# assumption that, by default, you'd rather use the version you paid | |||||
# good money for in preference to whatever Microsoft makes available | |||||
# for free. | |||||
# | |||||
# If you update this list, update _VCVER and _VCVER_TO_PRODUCT_DIR in | |||||
# Tool/MSCommon/vc.py, and the MSVC_VERSION documentation in Tool/msvc.xml. | |||||
SupportedVSList = [ | |||||
# Visual Studio 2017 | |||||
VisualStudio('14.1', | |||||
vc_version='14.1', | |||||
sdk_version='10.0A', | |||||
hkeys=[], | |||||
common_tools_var='VS150COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'VC\Auxiliary\Build\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64', "arm"], | |||||
), | |||||
# Visual Studio 2015 | |||||
VisualStudio('14.0', | |||||
vc_version='14.0', | |||||
sdk_version='10.0', | |||||
hkeys=[r'Microsoft\VisualStudio\14.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS140COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64', "arm"], | |||||
), | |||||
# Visual C++ 2015 Express Edition (for Desktop) | |||||
VisualStudio('14.0Exp', | |||||
vc_version='14.0', | |||||
sdk_version='10.0A', | |||||
hkeys=[r'Microsoft\VisualStudio\14.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS140COMNTOOLS', | |||||
executable_path=r'Common7\IDE\WDExpress.exe', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64', "arm"], | |||||
), | |||||
# Visual Studio 2013 | |||||
VisualStudio('12.0', | |||||
vc_version='12.0', | |||||
sdk_version='8.1A', | |||||
hkeys=[r'Microsoft\VisualStudio\12.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS120COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64'], | |||||
), | |||||
# Visual C++ 2013 Express Edition (for Desktop) | |||||
VisualStudio('12.0Exp', | |||||
vc_version='12.0', | |||||
sdk_version='8.1A', | |||||
hkeys=[r'Microsoft\VisualStudio\12.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS120COMNTOOLS', | |||||
executable_path=r'Common7\IDE\WDExpress.exe', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64'], | |||||
), | |||||
# Visual Studio 2012 | |||||
VisualStudio('11.0', | |||||
sdk_version='8.0A', | |||||
hkeys=[r'Microsoft\VisualStudio\11.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS110COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64'], | |||||
), | |||||
# Visual C++ 2012 Express Edition (for Desktop) | |||||
VisualStudio('11.0Exp', | |||||
vc_version='11.0', | |||||
sdk_version='8.0A', | |||||
hkeys=[r'Microsoft\VisualStudio\11.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS110COMNTOOLS', | |||||
executable_path=r'Common7\IDE\WDExpress.exe', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64'], | |||||
), | |||||
# Visual Studio 2010 | |||||
VisualStudio('10.0', | |||||
sdk_version='7.0A', | |||||
hkeys=[r'Microsoft\VisualStudio\10.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS100COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64'], | |||||
), | |||||
# Visual C++ 2010 Express Edition | |||||
VisualStudio('10.0Exp', | |||||
vc_version='10.0', | |||||
sdk_version='7.0A', | |||||
hkeys=[r'Microsoft\VCExpress\10.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS100COMNTOOLS', | |||||
executable_path=r'Common7\IDE\VCExpress.exe', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86'], | |||||
), | |||||
# Visual Studio 2008 | |||||
VisualStudio('9.0', | |||||
sdk_version='6.0A', | |||||
hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS90COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86', 'amd64'], | |||||
), | |||||
# Visual C++ 2008 Express Edition | |||||
VisualStudio('9.0Exp', | |||||
vc_version='9.0', | |||||
sdk_version='6.0A', | |||||
hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS90COMNTOOLS', | |||||
executable_path=r'Common7\IDE\VCExpress.exe', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
supported_arch=['x86'], | |||||
), | |||||
# Visual Studio 2005 | |||||
VisualStudio('8.0', | |||||
sdk_version='6.0A', | |||||
hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS80COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
default_dirname='Microsoft Visual Studio 8', | |||||
supported_arch=['x86', 'amd64'], | |||||
), | |||||
# Visual C++ 2005 Express Edition | |||||
VisualStudio('8.0Exp', | |||||
vc_version='8.0Exp', | |||||
sdk_version='6.0A', | |||||
hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS80COMNTOOLS', | |||||
executable_path=r'Common7\IDE\VCExpress.exe', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
default_dirname='Microsoft Visual Studio 8', | |||||
supported_arch=['x86'], | |||||
), | |||||
# Visual Studio .NET 2003 | |||||
VisualStudio('7.1', | |||||
sdk_version='6.0', | |||||
hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'], | |||||
common_tools_var='VS71COMNTOOLS', | |||||
executable_path=r'Common7\IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
default_dirname='Microsoft Visual Studio .NET 2003', | |||||
supported_arch=['x86'], | |||||
), | |||||
# Visual Studio .NET | |||||
VisualStudio('7.0', | |||||
sdk_version='2003R2', | |||||
hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'], | |||||
common_tools_var='VS70COMNTOOLS', | |||||
executable_path=r'IDE\devenv.com', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
default_dirname='Microsoft Visual Studio .NET', | |||||
supported_arch=['x86'], | |||||
), | |||||
# Visual Studio 6.0 | |||||
VisualStudio('6.0', | |||||
sdk_version='2003R1', | |||||
hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir', | |||||
'use_dir'], | |||||
common_tools_var='VS60COMNTOOLS', | |||||
executable_path=r'Common\MSDev98\Bin\MSDEV.COM', | |||||
batch_file_path=r'Common7\Tools\vsvars32.bat', | |||||
default_dirname='Microsoft Visual Studio', | |||||
supported_arch=['x86'], | |||||
), | |||||
] | |||||
SupportedVSMap = {} | |||||
for vs in SupportedVSList: | |||||
SupportedVSMap[vs.version] = vs | |||||
# Finding installed versions of Visual Studio isn't cheap, because it | |||||
# goes not only to the registry but also to the disk to sanity-check | |||||
# that there is, in fact, a Visual Studio directory there and that the | |||||
# registry entry isn't just stale. Find this information once, when | |||||
# requested, and cache it. | |||||
InstalledVSList = None | |||||
InstalledVSMap = None | |||||
def get_installed_visual_studios(): | |||||
global InstalledVSList | |||||
global InstalledVSMap | |||||
if InstalledVSList is None: | |||||
InstalledVSList = [] | |||||
InstalledVSMap = {} | |||||
for vs in SupportedVSList: | |||||
debug('trying to find VS %s' % vs.version) | |||||
if vs.get_executable(): | |||||
debug('found VS %s' % vs.version) | |||||
InstalledVSList.append(vs) | |||||
InstalledVSMap[vs.version] = vs | |||||
return InstalledVSList | |||||
def reset_installed_visual_studios(): | |||||
global InstalledVSList | |||||
global InstalledVSMap | |||||
InstalledVSList = None | |||||
InstalledVSMap = None | |||||
for vs in SupportedVSList: | |||||
vs.reset() | |||||
# Need to clear installed VC's as well as they are used in finding | |||||
# installed VS's | |||||
SCons.Tool.MSCommon.vc.reset_installed_vcs() | |||||
# We may be asked to update multiple construction environments with | |||||
# SDK information. When doing this, we check on-disk for whether | |||||
# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk | |||||
# is expensive, cache results by directory. | |||||
#SDKEnvironmentUpdates = {} | |||||
# | |||||
#def set_sdk_by_directory(env, sdk_dir): | |||||
# global SDKEnvironmentUpdates | |||||
# try: | |||||
# env_tuple_list = SDKEnvironmentUpdates[sdk_dir] | |||||
# except KeyError: | |||||
# env_tuple_list = [] | |||||
# SDKEnvironmentUpdates[sdk_dir] = env_tuple_list | |||||
# | |||||
# include_path = os.path.join(sdk_dir, 'include') | |||||
# mfc_path = os.path.join(include_path, 'mfc') | |||||
# atl_path = os.path.join(include_path, 'atl') | |||||
# | |||||
# if os.path.exists(mfc_path): | |||||
# env_tuple_list.append(('INCLUDE', mfc_path)) | |||||
# if os.path.exists(atl_path): | |||||
# env_tuple_list.append(('INCLUDE', atl_path)) | |||||
# env_tuple_list.append(('INCLUDE', include_path)) | |||||
# | |||||
# env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) | |||||
# env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) | |||||
# env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) | |||||
# | |||||
# for variable, directory in env_tuple_list: | |||||
# env.PrependENVPath(variable, directory) | |||||
def msvs_exists(): | |||||
return (len(get_installed_visual_studios()) > 0) | |||||
def get_vs_by_version(msvs): | |||||
global InstalledVSMap | |||||
global SupportedVSMap | |||||
debug('vs.py:get_vs_by_version()') | |||||
if msvs not in SupportedVSMap: | |||||
msg = "Visual Studio version %s is not supported" % repr(msvs) | |||||
raise SCons.Errors.UserError(msg) | |||||
get_installed_visual_studios() | |||||
vs = InstalledVSMap.get(msvs) | |||||
debug('InstalledVSMap:%s'%InstalledVSMap) | |||||
debug('vs.py:get_vs_by_version: found vs:%s'%vs) | |||||
# Some check like this would let us provide a useful error message | |||||
# if they try to set a Visual Studio version that's not installed. | |||||
# However, we also want to be able to run tests (like the unit | |||||
# tests) on systems that don't, or won't ever, have it installed. | |||||
# It might be worth resurrecting this, with some configurable | |||||
# setting that the tests can use to bypass the check. | |||||
#if not vs: | |||||
# msg = "Visual Studio version %s is not installed" % repr(msvs) | |||||
# raise SCons.Errors.UserError, msg | |||||
return vs | |||||
def get_default_version(env): | |||||
"""Returns the default version string to use for MSVS. | |||||
If no version was requested by the user through the MSVS environment | |||||
variable, query all the available visual studios through | |||||
get_installed_visual_studios, and take the highest one. | |||||
Return | |||||
------ | |||||
version: str | |||||
the default version. | |||||
""" | |||||
if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']): | |||||
# get all versions, and remember them for speed later | |||||
versions = [vs.version for vs in get_installed_visual_studios()] | |||||
env['MSVS'] = {'VERSIONS' : versions} | |||||
else: | |||||
versions = env['MSVS'].get('VERSIONS', []) | |||||
if 'MSVS_VERSION' not in env: | |||||
if versions: | |||||
env['MSVS_VERSION'] = versions[0] #use highest version by default | |||||
else: | |||||
debug('get_default_version: WARNING: no installed versions found, ' | |||||
'using first in SupportedVSList (%s)'%SupportedVSList[0].version) | |||||
env['MSVS_VERSION'] = SupportedVSList[0].version | |||||
env['MSVS']['VERSION'] = env['MSVS_VERSION'] | |||||
return env['MSVS_VERSION'] | |||||
def get_default_arch(env): | |||||
"""Return the default arch to use for MSVS | |||||
if no version was requested by the user through the MSVS_ARCH environment | |||||
variable, select x86 | |||||
Return | |||||
------ | |||||
arch: str | |||||
""" | |||||
arch = env.get('MSVS_ARCH', 'x86') | |||||
msvs = InstalledVSMap.get(env['MSVS_VERSION']) | |||||
if not msvs: | |||||
arch = 'x86' | |||||
elif not arch in msvs.get_supported_arch(): | |||||
fmt = "Visual Studio version %s does not support architecture %s" | |||||
raise SCons.Errors.UserError(fmt % (env['MSVS_VERSION'], arch)) | |||||
return arch | |||||
def merge_default_version(env): | |||||
version = get_default_version(env) | |||||
arch = get_default_arch(env) | |||||
def msvs_setup_env(env): | |||||
batfilename = msvs.get_batch_file() | |||||
msvs = get_vs_by_version(version) | |||||
if msvs is None: | |||||
return | |||||
# XXX: I think this is broken. This will silently set a bogus tool instead | |||||
# of failing, but there is no other way with the current scons tool | |||||
# framework | |||||
if batfilename is not None: | |||||
vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE') | |||||
msvs_list = get_installed_visual_studios() | |||||
vscommonvarnames = [vs.common_tools_var for vs in msvs_list] | |||||
save_ENV = env['ENV'] | |||||
nenv = normalize_env(env['ENV'], | |||||
['COMSPEC'] + vscommonvarnames, | |||||
force=True) | |||||
try: | |||||
output = get_output(batfilename, arch, env=nenv) | |||||
finally: | |||||
env['ENV'] = save_ENV | |||||
vars = parse_output(output, vars) | |||||
for k, v in vars.items(): | |||||
env.PrependENVPath(k, v, delete_existing=1) | |||||
def query_versions(): | |||||
"""Query the system to get available versions of VS. A version is | |||||
considered when a batfile is found.""" | |||||
msvs_list = get_installed_visual_studios() | |||||
versions = [msvs.version for msvs in msvs_list] | |||||
return versions | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,115 @@ | |||||
"""SCons.Tool.PharLapCommon | |||||
This module contains common code used by all Tools for the | |||||
Phar Lap ETS tool chain. Right now, this is linkloc and | |||||
386asm. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/PharLapCommon.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import os.path | |||||
import SCons.Errors | |||||
import SCons.Util | |||||
import re | |||||
def getPharLapPath(): | |||||
"""Reads the registry to find the installed path of the Phar Lap ETS | |||||
development kit. | |||||
Raises UserError if no installed version of Phar Lap can | |||||
be found.""" | |||||
if not SCons.Util.can_read_reg: | |||||
raise SCons.Errors.InternalError("No Windows registry module was found") | |||||
try: | |||||
k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, | |||||
'SOFTWARE\\Pharlap\\ETS') | |||||
val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir') | |||||
# The following is a hack...there is (not surprisingly) | |||||
# an odd issue in the Phar Lap plug in that inserts | |||||
# a bunch of junk data after the phar lap path in the | |||||
# registry. We must trim it. | |||||
idx=val.find('\0') | |||||
if idx >= 0: | |||||
val = val[:idx] | |||||
return os.path.normpath(val) | |||||
except SCons.Util.RegError: | |||||
raise SCons.Errors.UserError("Cannot find Phar Lap ETS path in the registry. Is it installed properly?") | |||||
REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)') | |||||
def getPharLapVersion(): | |||||
"""Returns the version of the installed ETS Tool Suite as a | |||||
decimal number. This version comes from the ETS_VER #define in | |||||
the embkern.h header. For example, '#define ETS_VER 1010' (which | |||||
is what Phar Lap 10.1 defines) would cause this method to return | |||||
1010. Phar Lap 9.1 does not have such a #define, but this method | |||||
will return 910 as a default. | |||||
Raises UserError if no installed version of Phar Lap can | |||||
be found.""" | |||||
include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) | |||||
if not os.path.exists(include_path): | |||||
raise SCons.Errors.UserError("Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?") | |||||
mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) | |||||
if mo: | |||||
return int(mo.group(1)) | |||||
# Default return for Phar Lap 9.1 | |||||
return 910 | |||||
def addPharLapPaths(env): | |||||
"""This function adds the path to the Phar Lap binaries, includes, | |||||
and libraries, if they are not already there.""" | |||||
ph_path = getPharLapPath() | |||||
try: | |||||
env_dict = env['ENV'] | |||||
except KeyError: | |||||
env_dict = {} | |||||
env['ENV'] = env_dict | |||||
SCons.Util.AddPathIfNotExists(env_dict, 'PATH', | |||||
os.path.join(ph_path, 'bin')) | |||||
SCons.Util.AddPathIfNotExists(env_dict, 'INCLUDE', | |||||
os.path.join(ph_path, 'include')) | |||||
SCons.Util.AddPathIfNotExists(env_dict, 'LIB', | |||||
os.path.join(ph_path, 'lib')) | |||||
SCons.Util.AddPathIfNotExists(env_dict, 'LIB', | |||||
os.path.join(ph_path, os.path.normpath('lib/vclib'))) | |||||
env['PHARLAP_PATH'] = getPharLapPath() | |||||
env['PHARLAP_VERSION'] = str(getPharLapVersion()) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,43 @@ | |||||
"""SCons.Tool.aixc++ | |||||
Tool-specific initialization for IBM xlC / Visual Age C++ compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/aixc++.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
#forward proxy to the preffered cxx version | |||||
from SCons.Tool.aixcxx import * | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,74 @@ | |||||
"""SCons.Tool.aixcc | |||||
Tool-specific initialization for IBM xlc / Visual Age C compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/aixcc.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os.path | |||||
import SCons.Platform.aix | |||||
from . import cc | |||||
packages = ['vac.C', 'ibmcxx.cmp'] | |||||
def get_xlc(env): | |||||
xlc = env.get('CC', 'xlc') | |||||
return SCons.Platform.aix.get_xlc(env, xlc, packages) | |||||
def generate(env): | |||||
"""Add Builders and construction variables for xlc / Visual Age | |||||
suite to an Environment.""" | |||||
path, _cc, version = get_xlc(env) | |||||
if path and _cc: | |||||
_cc = os.path.join(path, _cc) | |||||
if 'CC' not in env: | |||||
env['CC'] = _cc | |||||
cc.generate(env) | |||||
if version: | |||||
env['CCVERSION'] = version | |||||
def exists(env): | |||||
path, _cc, version = get_xlc(env) | |||||
if path and _cc: | |||||
xlc = os.path.join(path, _cc) | |||||
if os.path.exists(xlc): | |||||
return xlc | |||||
return None | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,77 @@ | |||||
"""SCons.Tool.aixc++ | |||||
Tool-specific initialization for IBM xlC / Visual Age C++ compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/aixcxx.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os.path | |||||
import SCons.Platform.aix | |||||
import SCons.Tool.cxx | |||||
cplusplus = SCons.Tool.cxx | |||||
#cplusplus = __import__('cxx', globals(), locals(), []) | |||||
packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp'] | |||||
def get_xlc(env): | |||||
xlc = env.get('CXX', 'xlC') | |||||
return SCons.Platform.aix.get_xlc(env, xlc, packages) | |||||
def generate(env): | |||||
"""Add Builders and construction variables for xlC / Visual Age | |||||
suite to an Environment.""" | |||||
path, _cxx, version = get_xlc(env) | |||||
if path and _cxx: | |||||
_cxx = os.path.join(path, _cxx) | |||||
if 'CXX' not in env: | |||||
env['CXX'] = _cxx | |||||
cplusplus.generate(env) | |||||
if version: | |||||
env['CXXVERSION'] = version | |||||
def exists(env): | |||||
path, _cxx, version = get_xlc(env) | |||||
if path and _cxx: | |||||
xlc = os.path.join(path, _cxx) | |||||
if os.path.exists(xlc): | |||||
return xlc | |||||
return None | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,80 @@ | |||||
"""engine.SCons.Tool.aixf77 | |||||
Tool-specific initialization for IBM Visual Age f77 Fortran compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/aixf77.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os.path | |||||
#import SCons.Platform.aix | |||||
from . import f77 | |||||
# It would be good to look for the AIX F77 package the same way we're now | |||||
# looking for the C and C++ packages. This should be as easy as supplying | |||||
# the correct package names in the following list and uncommenting the | |||||
# SCons.Platform.aix_get_xlc() call in the function below. | |||||
packages = [] | |||||
def get_xlf77(env): | |||||
xlf77 = env.get('F77', 'xlf77') | |||||
xlf77_r = env.get('SHF77', 'xlf77_r') | |||||
#return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages) | |||||
return (None, xlf77, xlf77_r, None) | |||||
def generate(env): | |||||
""" | |||||
Add Builders and construction variables for the Visual Age FORTRAN | |||||
compiler to an Environment. | |||||
""" | |||||
path, _f77, _shf77, version = get_xlf77(env) | |||||
if path: | |||||
_f77 = os.path.join(path, _f77) | |||||
_shf77 = os.path.join(path, _shf77) | |||||
f77.generate(env) | |||||
env['F77'] = _f77 | |||||
env['SHF77'] = _shf77 | |||||
def exists(env): | |||||
path, _f77, _shf77, version = get_xlf77(env) | |||||
if path and _f77: | |||||
xlf77 = os.path.join(path, _f77) | |||||
if os.path.exists(xlf77): | |||||
return xlf77 | |||||
return None | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,81 @@ | |||||
"""SCons.Tool.aixlink | |||||
Tool-specific initialization for the IBM Visual Age linker. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/aixlink.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import os.path | |||||
import SCons.Util | |||||
from . import aixcc | |||||
from . import link | |||||
import SCons.Tool.cxx | |||||
cplusplus = SCons.Tool.cxx | |||||
#cplusplus = __import__('cxx', globals(), locals(), []) | |||||
def smart_linkflags(source, target, env, for_signature): | |||||
if cplusplus.iscplusplus(source): | |||||
build_dir = env.subst('$BUILDDIR', target=target, source=source) | |||||
if build_dir: | |||||
return '-qtempinc=' + os.path.join(build_dir, 'tempinc') | |||||
return '' | |||||
def generate(env): | |||||
""" | |||||
Add Builders and construction variables for Visual Age linker to | |||||
an Environment. | |||||
""" | |||||
link.generate(env) | |||||
env['SMARTLINKFLAGS'] = smart_linkflags | |||||
env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') | |||||
env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') | |||||
env['SHLIBSUFFIX'] = '.a' | |||||
def exists(env): | |||||
# TODO: sync with link.smart_link() to choose a linker | |||||
linkers = { 'CXX': ['aixc++'], 'CC': ['aixcc'] } | |||||
alltools = [] | |||||
for langvar, linktools in linkers.items(): | |||||
if langvar in env: # use CC over CXX when user specified CC but not CXX | |||||
return SCons.Tool.FindTool(linktools, env) | |||||
alltools.extend(linktools) | |||||
return SCons.Tool.FindTool(alltools, env) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,79 @@ | |||||
"""SCons.Tool.applelink | |||||
Tool-specific initialization for the Apple gnu-like linker. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/applelink.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Util | |||||
# Even though the Mac is based on the GNU toolchain, it doesn't understand | |||||
# the -rpath option, so we use the "link" tool instead of "gnulink". | |||||
from . import link | |||||
def generate(env): | |||||
"""Add Builders and construction variables for applelink to an | |||||
Environment.""" | |||||
link.generate(env) | |||||
env['FRAMEWORKPATHPREFIX'] = '-F' | |||||
env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' | |||||
env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' | |||||
env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' | |||||
env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') | |||||
env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' | |||||
# TODO: Work needed to generate versioned shared libraries | |||||
# Leaving this commented out, and also going to disable versioned library checking for now | |||||
# see: http://docstore.mik.ua/orelly/unix3/mac/ch05_04.htm for proper naming | |||||
#link._setup_versioned_lib_variables(env, tool = 'applelink')#, use_soname = use_soname) | |||||
#env['LINKCALLBACKS'] = link._versioned_lib_callbacks() | |||||
# override the default for loadable modules, which are different | |||||
# on OS X than dynamic shared libs. echoing what XCode does for | |||||
# pre/suffixes: | |||||
env['LDMODULEPREFIX'] = '' | |||||
env['LDMODULESUFFIX'] = '' | |||||
env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') | |||||
env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' | |||||
def exists(env): | |||||
return env['PLATFORM'] == 'darwin' | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,63 @@ | |||||
"""SCons.Tool.ar | |||||
Tool-specific initialization for ar (library archive). | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/ar.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Defaults | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
def generate(env): | |||||
"""Add Builders and construction variables for ar to an Environment.""" | |||||
SCons.Tool.createStaticLibBuilder(env) | |||||
env['AR'] = 'ar' | |||||
env['ARFLAGS'] = SCons.Util.CLVar('rc') | |||||
env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' | |||||
env['LIBPREFIX'] = 'lib' | |||||
env['LIBSUFFIX'] = '.a' | |||||
if env.get('RANLIB',env.Detect('ranlib')) : | |||||
env['RANLIB'] = env.get('RANLIB','ranlib') | |||||
env['RANLIBFLAGS'] = SCons.Util.CLVar('') | |||||
env['RANLIBCOM'] = '$RANLIB $RANLIBFLAGS $TARGET' | |||||
def exists(env): | |||||
return env.Detect('ar') | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,78 @@ | |||||
"""SCons.Tool.as | |||||
Tool-specific initialization for as, the generic Posix assembler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/as.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Defaults | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
assemblers = ['as'] | |||||
ASSuffixes = ['.s', '.asm', '.ASM'] | |||||
ASPPSuffixes = ['.spp', '.SPP', '.sx'] | |||||
if SCons.Util.case_sensitive_suffixes('.s', '.S'): | |||||
ASPPSuffixes.extend(['.S']) | |||||
else: | |||||
ASSuffixes.extend(['.S']) | |||||
def generate(env): | |||||
"""Add Builders and construction variables for as to an Environment.""" | |||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env) | |||||
for suffix in ASSuffixes: | |||||
static_obj.add_action(suffix, SCons.Defaults.ASAction) | |||||
shared_obj.add_action(suffix, SCons.Defaults.ASAction) | |||||
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) | |||||
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) | |||||
for suffix in ASPPSuffixes: | |||||
static_obj.add_action(suffix, SCons.Defaults.ASPPAction) | |||||
shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) | |||||
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) | |||||
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) | |||||
env['AS'] = env.Detect(assemblers) or 'as' | |||||
env['ASFLAGS'] = SCons.Util.CLVar('') | |||||
env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' | |||||
env['ASPPFLAGS'] = '$ASFLAGS' | |||||
env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' | |||||
def exists(env): | |||||
return env.Detect(assemblers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,81 @@ | |||||
"""SCons.Tool.bcc32 | |||||
XXX | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/bcc32.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import os.path | |||||
import SCons.Defaults | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
def findIt(program, env): | |||||
# First search in the SCons path and then the OS path: | |||||
borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) | |||||
if borwin: | |||||
dir = os.path.dirname(borwin) | |||||
env.PrependENVPath('PATH', dir) | |||||
return borwin | |||||
def generate(env): | |||||
findIt('bcc32', env) | |||||
"""Add Builders and construction variables for bcc to an | |||||
Environment.""" | |||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env) | |||||
for suffix in ['.c', '.cpp']: | |||||
static_obj.add_action(suffix, SCons.Defaults.CAction) | |||||
shared_obj.add_action(suffix, SCons.Defaults.ShCAction) | |||||
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) | |||||
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) | |||||
env['CC'] = 'bcc32' | |||||
env['CCFLAGS'] = SCons.Util.CLVar('') | |||||
env['CFLAGS'] = SCons.Util.CLVar('') | |||||
env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' | |||||
env['SHCC'] = '$CC' | |||||
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') | |||||
env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') | |||||
env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' | |||||
env['CPPDEFPREFIX'] = '-D' | |||||
env['CPPDEFSUFFIX'] = '' | |||||
env['INCPREFIX'] = '-I' | |||||
env['INCSUFFIX'] = '' | |||||
env['SHOBJSUFFIX'] = '.dll' | |||||
env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 | |||||
env['CFILESUFFIX'] = '.cpp' | |||||
def exists(env): | |||||
return findIt('bcc32', env) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,44 @@ | |||||
"""SCons.Tool.c++ | |||||
Tool-specific initialization for generic Posix C++ compilers. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/c++.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
#forward proxy to the preffered cxx version | |||||
from SCons.Tool.cxx import * | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,105 @@ | |||||
"""SCons.Tool.cc | |||||
Tool-specific initialization for generic Posix C compilers. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/cc.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Tool | |||||
import SCons.Defaults | |||||
import SCons.Util | |||||
CSuffixes = ['.c', '.m'] | |||||
if not SCons.Util.case_sensitive_suffixes('.c', '.C'): | |||||
CSuffixes.append('.C') | |||||
def add_common_cc_variables(env): | |||||
""" | |||||
Add underlying common "C compiler" variables that | |||||
are used by multiple tools (specifically, c++). | |||||
""" | |||||
if '_CCCOMCOM' not in env: | |||||
env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' | |||||
# It's a hack to test for darwin here, but the alternative | |||||
# of creating an applecc.py to contain this seems overkill. | |||||
# Maybe someday the Apple platform will require more setup and | |||||
# this logic will be moved. | |||||
env['FRAMEWORKS'] = SCons.Util.CLVar('') | |||||
env['FRAMEWORKPATH'] = SCons.Util.CLVar('') | |||||
if env['PLATFORM'] == 'darwin': | |||||
env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' | |||||
if 'CCFLAGS' not in env: | |||||
env['CCFLAGS'] = SCons.Util.CLVar('') | |||||
if 'SHCCFLAGS' not in env: | |||||
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') | |||||
compilers = ['cc'] | |||||
def generate(env): | |||||
""" | |||||
Add Builders and construction variables for C compilers to an Environment. | |||||
""" | |||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env) | |||||
for suffix in CSuffixes: | |||||
static_obj.add_action(suffix, SCons.Defaults.CAction) | |||||
shared_obj.add_action(suffix, SCons.Defaults.ShCAction) | |||||
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) | |||||
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) | |||||
add_common_cc_variables(env) | |||||
if 'CC' not in env: | |||||
env['CC'] = env.Detect(compilers) or compilers[0] | |||||
env['CFLAGS'] = SCons.Util.CLVar('') | |||||
env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' | |||||
env['SHCC'] = '$CC' | |||||
env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') | |||||
env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' | |||||
env['CPPDEFPREFIX'] = '-D' | |||||
env['CPPDEFSUFFIX'] = '' | |||||
env['INCPREFIX'] = '-I' | |||||
env['INCSUFFIX'] = '' | |||||
env['SHOBJSUFFIX'] = '.os' | |||||
env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 | |||||
env['CFILESUFFIX'] = '.c' | |||||
def exists(env): | |||||
return env.Detect(env.get('CC', compilers)) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,83 @@ | |||||
# -*- coding: utf-8; -*- | |||||
"""SCons.Tool.clang | |||||
Tool-specific initialization for clang. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
# __revision__ = "src/engine/SCons/Tool/clang.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
# Based on SCons/Tool/gcc.py by Paweł Tomulik 2014 as a separate tool. | |||||
# Brought into the SCons mainline by Russel Winder 2017. | |||||
import os | |||||
import re | |||||
import subprocess | |||||
import sys | |||||
import SCons.Util | |||||
import SCons.Tool.cc | |||||
compilers = ['clang'] | |||||
def generate(env): | |||||
"""Add Builders and construction variables for clang to an Environment.""" | |||||
SCons.Tool.cc.generate(env) | |||||
env['CC'] = env.Detect(compilers) or 'clang' | |||||
if env['PLATFORM'] in ['cygwin', 'win32']: | |||||
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') | |||||
else: | |||||
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') | |||||
# determine compiler version | |||||
if env['CC']: | |||||
#pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], | |||||
pipe = SCons.Action._subproc(env, [env['CC'], '--version'], | |||||
stdin='devnull', | |||||
stderr='devnull', | |||||
stdout=subprocess.PIPE) | |||||
if pipe.wait() != 0: return | |||||
# clang -dumpversion is of no use | |||||
line = pipe.stdout.readline() | |||||
if sys.version_info[0] > 2: | |||||
line = line.decode() | |||||
match = re.search(r'clang +version +([0-9]+(?:\.[0-9]+)+)', line) | |||||
if match: | |||||
env['CCVERSION'] = match.group(1) | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,91 @@ | |||||
# -*- coding: utf-8; -*- | |||||
"""SCons.Tool.clang++ | |||||
Tool-specific initialization for clang++. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
# __revision__ = "src/engine/SCons/Tool/clangxx.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
# Based on SCons/Tool/g++.py by Paweł Tomulik 2014 as a separate tool. | |||||
# Brought into the SCons mainline by Russel Winder 2017. | |||||
import os.path | |||||
import re | |||||
import subprocess | |||||
import sys | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
import SCons.Tool.cxx | |||||
compilers = ['clang++'] | |||||
def generate(env): | |||||
"""Add Builders and construction variables for clang++ to an Environment.""" | |||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env) | |||||
SCons.Tool.cxx.generate(env) | |||||
env['CXX'] = env.Detect(compilers) or 'clang++' | |||||
# platform specific settings | |||||
if env['PLATFORM'] == 'aix': | |||||
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') | |||||
env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 | |||||
env['SHOBJSUFFIX'] = '$OBJSUFFIX' | |||||
elif env['PLATFORM'] == 'hpux': | |||||
env['SHOBJSUFFIX'] = '.pic.o' | |||||
elif env['PLATFORM'] == 'sunos': | |||||
env['SHOBJSUFFIX'] = '.pic.o' | |||||
# determine compiler version | |||||
if env['CXX']: | |||||
pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], | |||||
stdin='devnull', | |||||
stderr='devnull', | |||||
stdout=subprocess.PIPE) | |||||
if pipe.wait() != 0: return | |||||
# clang -dumpversion is of no use | |||||
line = pipe.stdout.readline() | |||||
if sys.version_info[0] > 2: | |||||
line = line.decode() | |||||
match = re.search(r'clang +version +([0-9]+(?:\.[0-9]+)+)', line) | |||||
if match: | |||||
env['CXXVERSION'] = match.group(1) | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,58 @@ | |||||
"""engine.SCons.Tool.cvf | |||||
Tool-specific initialization for the Compaq Visual Fortran compiler. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/cvf.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
from . import fortran | |||||
compilers = ['f90'] | |||||
def generate(env): | |||||
"""Add Builders and construction variables for compaq visual fortran to an Environment.""" | |||||
fortran.generate(env) | |||||
env['FORTRAN'] = 'f90' | |||||
env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' | |||||
env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' | |||||
env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' | |||||
env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' | |||||
env['OBJSUFFIX'] = '.obj' | |||||
env['FORTRANMODDIR'] = '${TARGET.dir}' | |||||
env['FORTRANMODDIRPREFIX'] = '/module:' | |||||
env['FORTRANMODDIRSUFFIX'] = '' | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,100 @@ | |||||
"""SCons.Tool.c++ | |||||
Tool-specific initialization for generic Posix C++ compilers. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/cxx.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os.path | |||||
import SCons.Tool | |||||
import SCons.Defaults | |||||
import SCons.Util | |||||
compilers = ['CC', 'c++'] | |||||
CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++', '.mm'] | |||||
if SCons.Util.case_sensitive_suffixes('.c', '.C'): | |||||
CXXSuffixes.append('.C') | |||||
def iscplusplus(source): | |||||
if not source: | |||||
# Source might be None for unusual cases like SConf. | |||||
return 0 | |||||
for s in source: | |||||
if s.sources: | |||||
ext = os.path.splitext(str(s.sources[0]))[1] | |||||
if ext in CXXSuffixes: | |||||
return 1 | |||||
return 0 | |||||
def generate(env): | |||||
""" | |||||
Add Builders and construction variables for Visual Age C++ compilers | |||||
to an Environment. | |||||
""" | |||||
import SCons.Tool | |||||
import SCons.Tool.cc | |||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env) | |||||
for suffix in CXXSuffixes: | |||||
static_obj.add_action(suffix, SCons.Defaults.CXXAction) | |||||
shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) | |||||
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) | |||||
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) | |||||
SCons.Tool.cc.add_common_cc_variables(env) | |||||
if 'CXX' not in env: | |||||
env['CXX'] = env.Detect(compilers) or compilers[0] | |||||
env['CXXFLAGS'] = SCons.Util.CLVar('') | |||||
env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' | |||||
env['SHCXX'] = '$CXX' | |||||
env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') | |||||
env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' | |||||
env['CPPDEFPREFIX'] = '-D' | |||||
env['CPPDEFSUFFIX'] = '' | |||||
env['INCPREFIX'] = '-I' | |||||
env['INCSUFFIX'] = '' | |||||
env['SHOBJSUFFIX'] = '.os' | |||||
env['OBJSUFFIX'] = '.o' | |||||
env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 | |||||
env['CXXFILESUFFIX'] = '.cc' | |||||
def exists(env): | |||||
return env.Detect(env.get('CXX', compilers)) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,236 @@ | |||||
"""SCons.Tool.cyglink | |||||
Customization of gnulink for Cygwin (http://www.cygwin.com/) | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
from __future__ import absolute_import, print_function | |||||
import re | |||||
import os | |||||
import SCons.Action | |||||
import SCons.Util | |||||
import SCons.Tool | |||||
#MAYBE: from . import gnulink | |||||
from . import gnulink | |||||
from . import link | |||||
def _lib_generator(target, source, env, for_signature, **kw): | |||||
try: cmd = kw['cmd'] | |||||
except KeyError: cmd = SCons.Util.CLVar(['$SHLINK']) | |||||
try: vp = kw['varprefix'] | |||||
except KeyError: vp = 'SHLIB' | |||||
dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp) | |||||
if dll: cmd.extend(['-o', dll]) | |||||
cmd.extend(['$SHLINKFLAGS', '$__%sVERSIONFLAGS' % vp, '$__RPATH']) | |||||
implib = env.FindIxes(target, 'IMPLIBPREFIX', 'IMPLIBSUFFIX') | |||||
if implib: | |||||
cmd.extend([ | |||||
'-Wl,--out-implib='+implib.get_string(for_signature), | |||||
'-Wl,--export-all-symbols', | |||||
'-Wl,--enable-auto-import', | |||||
'-Wl,--whole-archive', '$SOURCES', | |||||
'-Wl,--no-whole-archive', '$_LIBDIRFLAGS', '$_LIBFLAGS' | |||||
]) | |||||
else: | |||||
cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) | |||||
return [cmd] | |||||
def shlib_generator(target, source, env, for_signature): | |||||
return _lib_generator(target, source, env, for_signature, | |||||
varprefix='SHLIB', | |||||
cmd = SCons.Util.CLVar(['$SHLINK'])) | |||||
def ldmod_generator(target, source, env, for_signature): | |||||
return _lib_generator(target, source, env, for_signature, | |||||
varprefix='LDMODULE', | |||||
cmd = SCons.Util.CLVar(['$LDMODULE'])) | |||||
def _lib_emitter(target, source, env, **kw): | |||||
Verbose = False | |||||
if Verbose: | |||||
print("_lib_emitter: target[0]=%r" % target[0].get_path()) | |||||
try: vp = kw['varprefix'] | |||||
except KeyError: vp = 'SHLIB' | |||||
try: libtype = kw['libtype'] | |||||
except KeyError: libtype = 'ShLib' | |||||
dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp) | |||||
no_import_lib = env.get('no_import_lib', 0) | |||||
if Verbose: | |||||
print("_lib_emitter: dll=%r" % dll.get_path()) | |||||
if not dll or len(target) > 1: | |||||
raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$%sSUFFIX" % vp)) | |||||
# Remove any "lib" after the prefix | |||||
pre = env.subst('$%sPREFIX' % vp) | |||||
if dll.name[len(pre):len(pre)+3] == 'lib': | |||||
dll.name = pre + dll.name[len(pre)+3:] | |||||
if Verbose: | |||||
print("_lib_emitter: dll.name=%r" % dll.name) | |||||
orig_target = target | |||||
target = [env.fs.File(dll)] | |||||
target[0].attributes.shared = 1 | |||||
if Verbose: | |||||
print("_lib_emitter: after target=[env.fs.File(dll)]: target[0]=%r" % target[0].get_path()) | |||||
# Append an import lib target | |||||
if not no_import_lib: | |||||
# Create list of target libraries as strings | |||||
target_strings = env.ReplaceIxes(orig_target[0], | |||||
'%sPREFIX' % vp, '%sSUFFIX' % vp, | |||||
'IMPLIBPREFIX', 'IMPLIBSUFFIX') | |||||
if Verbose: | |||||
print("_lib_emitter: target_strings=%r" % target_strings) | |||||
implib_target = env.fs.File(target_strings) | |||||
if Verbose: | |||||
print("_lib_emitter: implib_target=%r" % implib_target.get_path()) | |||||
implib_target.attributes.shared = 1 | |||||
target.append(implib_target) | |||||
symlinks = SCons.Tool.ImpLibSymlinkGenerator(env, implib_target, | |||||
implib_libtype=libtype, | |||||
generator_libtype=libtype+'ImpLib') | |||||
if Verbose: | |||||
print("_lib_emitter: implib symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks)) | |||||
if symlinks: | |||||
SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target, clean_targets = target[0]) | |||||
implib_target.attributes.shliblinks = symlinks | |||||
return (target, source) | |||||
def shlib_emitter(target, source, env): | |||||
return _lib_emitter(target, source, env, varprefix='SHLIB', libtype='ShLib') | |||||
def ldmod_emitter(target, source, env): | |||||
return _lib_emitter(target, source, env, varprefix='LDMODULE', libtype='LdMod') | |||||
def _versioned_lib_suffix(env, suffix, version): | |||||
"""Generate versioned shared library suffix from a unversioned one. | |||||
If suffix='.dll', and version='0.1.2', then it returns '-0-1-2.dll'""" | |||||
Verbose = False | |||||
if Verbose: | |||||
print("_versioned_lib_suffix: suffix= ", suffix) | |||||
print("_versioned_lib_suffix: version= ", version) | |||||
cygversion = re.sub('\.', '-', version) | |||||
if not suffix.startswith('-' + cygversion): | |||||
suffix = '-' + cygversion + suffix | |||||
if Verbose: | |||||
print("_versioned_lib_suffix: return suffix= ", suffix) | |||||
return suffix | |||||
def _versioned_implib_name(env, libnode, version, prefix, suffix, **kw): | |||||
return link._versioned_lib_name(env, libnode, version, prefix, suffix, | |||||
SCons.Tool.ImpLibPrefixGenerator, | |||||
SCons.Tool.ImpLibSuffixGenerator, | |||||
implib_libtype=kw['libtype']) | |||||
def _versioned_implib_symlinks(env, libnode, version, prefix, suffix, **kw): | |||||
"""Generate link names that should be created for a versioned shared library. | |||||
Returns a list in the form [ (link, linktarget), ... ] | |||||
""" | |||||
Verbose = False | |||||
if Verbose: | |||||
print("_versioned_implib_symlinks: libnode=%r" % libnode.get_path()) | |||||
print("_versioned_implib_symlinks: version=%r" % version) | |||||
try: libtype = kw['libtype'] | |||||
except KeyError: libtype = 'ShLib' | |||||
linkdir = os.path.dirname(libnode.get_path()) | |||||
if Verbose: | |||||
print("_versioned_implib_symlinks: linkdir=%r" % linkdir) | |||||
name = SCons.Tool.ImpLibNameGenerator(env, libnode, | |||||
implib_libtype=libtype, | |||||
generator_libtype=libtype+'ImpLib') | |||||
if Verbose: | |||||
print("_versioned_implib_symlinks: name=%r" % name) | |||||
major = version.split('.')[0] | |||||
link0 = env.fs.File(os.path.join(linkdir, name)) | |||||
symlinks = [(link0, libnode)] | |||||
if Verbose: | |||||
print("_versioned_implib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks)) | |||||
return symlinks | |||||
shlib_action = SCons.Action.Action(shlib_generator, generator=1) | |||||
ldmod_action = SCons.Action.Action(ldmod_generator, generator=1) | |||||
def generate(env): | |||||
"""Add Builders and construction variables for cyglink to an Environment.""" | |||||
gnulink.generate(env) | |||||
env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,-no-undefined') | |||||
env['SHLINKCOM'] = shlib_action | |||||
env['LDMODULECOM'] = ldmod_action | |||||
env.Append(SHLIBEMITTER = [shlib_emitter]) | |||||
env.Append(LDMODULEEMITTER = [ldmod_emitter]) | |||||
env['SHLIBPREFIX'] = 'cyg' | |||||
env['SHLIBSUFFIX'] = '.dll' | |||||
env['IMPLIBPREFIX'] = 'lib' | |||||
env['IMPLIBSUFFIX'] = '.dll.a' | |||||
# Variables used by versioned shared libraries | |||||
env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' | |||||
env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' | |||||
# SHLIBVERSIONFLAGS and LDMODULEVERSIONFLAGS are same as in gnulink... | |||||
# LINKCALLBACKS are NOT inherited from gnulink | |||||
env['LINKCALLBACKS'] = { | |||||
'VersionedShLibSuffix' : _versioned_lib_suffix, | |||||
'VersionedLdModSuffix' : _versioned_lib_suffix, | |||||
'VersionedImpLibSuffix' : _versioned_lib_suffix, | |||||
'VersionedShLibName' : link._versioned_shlib_name, | |||||
'VersionedLdModName' : link._versioned_ldmod_name, | |||||
'VersionedShLibImpLibName' : lambda *args: _versioned_implib_name(*args, libtype='ShLib'), | |||||
'VersionedLdModImpLibName' : lambda *args: _versioned_implib_name(*args, libtype='LdMod'), | |||||
'VersionedShLibImpLibSymlinks' : lambda *args: _versioned_implib_symlinks(*args, libtype='ShLib'), | |||||
'VersionedLdModImpLibSymlinks' : lambda *args: _versioned_implib_symlinks(*args, libtype='LdMod'), | |||||
} | |||||
# these variables were set by gnulink but are not used in cyglink | |||||
try: del env['_SHLIBSONAME'] | |||||
except KeyError: pass | |||||
try: del env['_LDMODULESONAME'] | |||||
except KeyError: pass | |||||
def exists(env): | |||||
return gnulink.exists(env) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,50 @@ | |||||
"""SCons.Tool.default | |||||
Initialization with a default tool list. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/default.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Tool | |||||
def generate(env): | |||||
"""Add default tools.""" | |||||
for t in SCons.Tool.tool_list(env['PLATFORM'], env): | |||||
SCons.Tool.Tool(t)(env) | |||||
def exists(env): | |||||
return 1 | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,161 @@ | |||||
from __future__ import print_function | |||||
"""SCons.Tool.dmd | |||||
Tool-specific initialization for the Digital Mars D compiler. | |||||
(http://digitalmars.com/d) | |||||
Originally coded by Andy Friesen (andy@ikagames.com) | |||||
15 November 2003 | |||||
Evolved by Russel Winder (russel@winder.org.uk) | |||||
2010-02-07 onwards | |||||
Compiler variables: | |||||
DC - The name of the D compiler to use. Defaults to dmd or gdmd, | |||||
whichever is found. | |||||
DPATH - List of paths to search for import modules. | |||||
DVERSIONS - List of version tags to enable when compiling. | |||||
DDEBUG - List of debug tags to enable when compiling. | |||||
Linker related variables: | |||||
LIBS - List of library files to link in. | |||||
DLINK - Name of the linker to use. Defaults to dmd or gdmd, | |||||
whichever is found. | |||||
DLINKFLAGS - List of linker flags. | |||||
Lib tool variables: | |||||
DLIB - Name of the lib tool to use. Defaults to lib. | |||||
DLIBFLAGS - List of flags to pass to the lib tool. | |||||
LIBS - Same as for the linker. (libraries to pull into the .lib) | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/dmd.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import os | |||||
import subprocess | |||||
import SCons.Action | |||||
import SCons.Builder | |||||
import SCons.Defaults | |||||
import SCons.Scanner.D | |||||
import SCons.Tool | |||||
import SCons.Tool.DCommon as DCommon | |||||
def generate(env): | |||||
static_obj, shared_obj = SCons.Tool.createObjBuilders(env) | |||||
static_obj.add_action('.d', SCons.Defaults.DAction) | |||||
shared_obj.add_action('.d', SCons.Defaults.ShDAction) | |||||
static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter) | |||||
shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter) | |||||
env['DC'] = env.Detect(['dmd', 'ldmd2', 'gdmd']) or 'dmd' | |||||
env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' | |||||
env['_DINCFLAGS'] = '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}' | |||||
env['_DVERFLAGS'] = '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}' | |||||
env['_DDEBUGFLAGS'] = '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}' | |||||
env['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' | |||||
env['SHDC'] = '$DC' | |||||
env['SHDCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -fPIC -of$TARGET $SOURCES' | |||||
env['DPATH'] = ['#/'] | |||||
env['DFLAGS'] = [] | |||||
env['DVERSIONS'] = [] | |||||
env['DDEBUG'] = [] | |||||
if env['DC']: | |||||
DCommon.addDPATHToEnv(env, env['DC']) | |||||
env['DINCPREFIX'] = '-I' | |||||
env['DINCSUFFIX'] = '' | |||||
env['DVERPREFIX'] = '-version=' | |||||
env['DVERSUFFIX'] = '' | |||||
env['DDEBUGPREFIX'] = '-debug=' | |||||
env['DDEBUGSUFFIX'] = '' | |||||
env['DFLAGPREFIX'] = '-' | |||||
env['DFLAGSUFFIX'] = '' | |||||
env['DFILESUFFIX'] = '.d' | |||||
env['DLINK'] = '$DC' | |||||
env['DLINKFLAGS'] = SCons.Util.CLVar('') | |||||
env['DLINKCOM'] = '$DLINK -of$TARGET $DLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' | |||||
env['SHDLINK'] = '$DC' | |||||
env['SHDLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared -defaultlib=libphobos2.so') | |||||
env['SHDLINKCOM'] = '$DLINK -of$TARGET $SHDLINKFLAGS $__SHDLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' | |||||
env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l' | |||||
env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else '' | |||||
env['_DLIBFLAGS'] = '${_stripixes(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' | |||||
env['DLIBDIRPREFIX'] = '-L-L' | |||||
env['DLIBDIRSUFFIX'] = '' | |||||
env['_DLIBDIRFLAGS'] = '${_concat(DLIBDIRPREFIX, LIBPATH, DLIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)}' | |||||
env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr' | |||||
env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '') | |||||
# env['_DLIBFLAGS'] = '${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)}' | |||||
env['DLIBFLAGPREFIX'] = '-' | |||||
env['DLIBFLAGSUFFIX'] = '' | |||||
# __RPATH is set to $_RPATH in the platform specification if that | |||||
# platform supports it. | |||||
env['DRPATHPREFIX'] = '-L-rpath,' if env['PLATFORM'] == 'darwin' else '-L-rpath=' | |||||
env['DRPATHSUFFIX'] = '' | |||||
env['_DRPATH'] = '${_concat(DRPATHPREFIX, RPATH, DRPATHSUFFIX, __env__)}' | |||||
# Support for versioned libraries | |||||
env['_SHDLIBVERSIONFLAGS'] = '$SHDLIBVERSIONFLAGS -L-soname=$_SHDLIBSONAME' | |||||
env['_SHDLIBSONAME'] = '${DShLibSonameGenerator(__env__,TARGET)}' | |||||
# NOTE: this is a quick hack, the soname will only work if there is | |||||
# c/c++ linker loaded which provides callback for the ShLibSonameGenerator | |||||
env['DShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator | |||||
# NOTE: this is only for further reference, currently $SHDLIBVERSION does | |||||
# not work, the user must use $SHLIBVERSION | |||||
env['SHDLIBVERSION'] = '$SHLIBVERSION' | |||||
env['SHDLIBVERSIONFLAGS'] = [] | |||||
env['BUILDERS']['ProgramAllAtOnce'] = SCons.Builder.Builder( | |||||
action='$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -of$TARGET $DLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS', | |||||
emitter=DCommon.allAtOnceEmitter, | |||||
) | |||||
def exists(env): | |||||
return env.Detect(['dmd', 'ldmd2', 'gdmd']) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,883 @@ | |||||
"""SCons.Tool.docbook | |||||
Tool-specific initialization for Docbook. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
import os | |||||
import glob | |||||
import re | |||||
import SCons.Action | |||||
import SCons.Builder | |||||
import SCons.Defaults | |||||
import SCons.Script | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
__debug_tool_location = False | |||||
# Get full path to this script | |||||
scriptpath = os.path.dirname(os.path.realpath(__file__)) | |||||
# Local folder for the collection of DocBook XSLs | |||||
db_xsl_folder = 'docbook-xsl-1.76.1' | |||||
# Do we have libxml2/libxslt/lxml? | |||||
has_libxml2 = True | |||||
has_lxml = True | |||||
try: | |||||
import libxml2 | |||||
import libxslt | |||||
except: | |||||
has_libxml2 = False | |||||
try: | |||||
import lxml | |||||
except: | |||||
has_lxml = False | |||||
# Set this to True, to prefer xsltproc over libxml2 and lxml | |||||
prefer_xsltproc = False | |||||
# Regexs for parsing Docbook XML sources of MAN pages | |||||
re_manvolnum = re.compile("<manvolnum>([^<]*)</manvolnum>") | |||||
re_refname = re.compile("<refname>([^<]*)</refname>") | |||||
# | |||||
# Helper functions | |||||
# | |||||
def __extend_targets_sources(target, source): | |||||
""" Prepare the lists of target and source files. """ | |||||
if not SCons.Util.is_List(target): | |||||
target = [target] | |||||
if not source: | |||||
source = target[:] | |||||
elif not SCons.Util.is_List(source): | |||||
source = [source] | |||||
if len(target) < len(source): | |||||
target.extend(source[len(target):]) | |||||
return target, source | |||||
def __init_xsl_stylesheet(kw, env, user_xsl_var, default_path): | |||||
if kw.get('DOCBOOK_XSL','') == '': | |||||
xsl_style = kw.get('xsl', env.subst(user_xsl_var)) | |||||
if xsl_style == '': | |||||
path_args = [scriptpath, db_xsl_folder] + default_path | |||||
xsl_style = os.path.join(*path_args) | |||||
kw['DOCBOOK_XSL'] = xsl_style | |||||
def __select_builder(lxml_builder, libxml2_builder, cmdline_builder): | |||||
""" Selects a builder, based on which Python modules are present. """ | |||||
if prefer_xsltproc: | |||||
return cmdline_builder | |||||
if not has_libxml2: | |||||
# At the moment we prefer libxml2 over lxml, the latter can lead | |||||
# to conflicts when installed together with libxml2. | |||||
if has_lxml: | |||||
return lxml_builder | |||||
else: | |||||
return cmdline_builder | |||||
return libxml2_builder | |||||
def __ensure_suffix(t, suffix): | |||||
""" Ensure that the target t has the given suffix. """ | |||||
tpath = str(t) | |||||
if not tpath.endswith(suffix): | |||||
return tpath+suffix | |||||
return t | |||||
def __ensure_suffix_stem(t, suffix): | |||||
""" Ensure that the target t has the given suffix, and return the file's stem. """ | |||||
tpath = str(t) | |||||
if not tpath.endswith(suffix): | |||||
stem = tpath | |||||
tpath += suffix | |||||
return tpath, stem | |||||
else: | |||||
stem, ext = os.path.splitext(tpath) | |||||
return t, stem | |||||
def __get_xml_text(root): | |||||
""" Return the text for the given root node (xml.dom.minidom). """ | |||||
txt = "" | |||||
for e in root.childNodes: | |||||
if (e.nodeType == e.TEXT_NODE): | |||||
txt += e.data | |||||
return txt | |||||
def __create_output_dir(base_dir): | |||||
""" Ensure that the output directory base_dir exists. """ | |||||
root, tail = os.path.split(base_dir) | |||||
dir = None | |||||
if tail: | |||||
if base_dir.endswith('/'): | |||||
dir = base_dir | |||||
else: | |||||
dir = root | |||||
else: | |||||
if base_dir.endswith('/'): | |||||
dir = base_dir | |||||
if dir and not os.path.isdir(dir): | |||||
os.makedirs(dir) | |||||
# | |||||
# Supported command line tools and their call "signature" | |||||
# | |||||
xsltproc_com_priority = ['xsltproc', 'saxon', 'saxon-xslt', 'xalan'] | |||||
# TODO: Set minimum version of saxon-xslt to be 8.x (lower than this only supports xslt 1.0. | |||||
# see: http://saxon.sourceforge.net/saxon6.5.5/ | |||||
# see: http://saxon.sourceforge.net/ | |||||
xsltproc_com = {'xsltproc' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE', | |||||
'saxon' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', | |||||
'saxon-xslt' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', | |||||
'xalan' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -q -out $TARGET -xsl $DOCBOOK_XSL -in $SOURCE'} | |||||
xmllint_com = {'xmllint' : '$DOCBOOK_XMLLINT $DOCBOOK_XMLLINTFLAGS --xinclude $SOURCE > $TARGET'} | |||||
fop_com = {'fop' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -fo $SOURCE -pdf $TARGET', | |||||
'xep' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -valid -fo $SOURCE -pdf $TARGET', | |||||
'jw' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -f docbook -b pdf $SOURCE -o $TARGET'} | |||||
def __detect_cl_tool(env, chainkey, cdict, cpriority=None): | |||||
""" | |||||
Helper function, picks a command line tool from the list | |||||
and initializes its environment variables. | |||||
""" | |||||
if env.get(chainkey,'') == '': | |||||
clpath = '' | |||||
if cpriority is None: | |||||
cpriority = cdict.keys() | |||||
for cltool in cpriority: | |||||
if __debug_tool_location: | |||||
print("DocBook: Looking for %s"%cltool) | |||||
clpath = env.WhereIs(cltool) | |||||
if clpath: | |||||
if __debug_tool_location: | |||||
print("DocBook: Found:%s"%cltool) | |||||
env[chainkey] = clpath | |||||
if not env[chainkey + 'COM']: | |||||
env[chainkey + 'COM'] = cdict[cltool] | |||||
break | |||||
def _detect(env): | |||||
""" | |||||
Detect all the command line tools that we might need for creating | |||||
the requested output formats. | |||||
""" | |||||
global prefer_xsltproc | |||||
if env.get('DOCBOOK_PREFER_XSLTPROC',''): | |||||
prefer_xsltproc = True | |||||
if ((not has_libxml2 and not has_lxml) or (prefer_xsltproc)): | |||||
# Try to find the XSLT processors | |||||
__detect_cl_tool(env, 'DOCBOOK_XSLTPROC', xsltproc_com, xsltproc_com_priority) | |||||
__detect_cl_tool(env, 'DOCBOOK_XMLLINT', xmllint_com) | |||||
__detect_cl_tool(env, 'DOCBOOK_FOP', fop_com, ['fop','xep','jw']) | |||||
# | |||||
# Scanners | |||||
# | |||||
include_re = re.compile('fileref\\s*=\\s*["|\']([^\\n]*)["|\']') | |||||
sentity_re = re.compile('<!ENTITY\\s+%*\\s*[^\\s]+\\s+SYSTEM\\s+["|\']([^\\n]*)["|\']>') | |||||
def __xml_scan(node, env, path, arg): | |||||
""" Simple XML file scanner, detecting local images and XIncludes as implicit dependencies. """ | |||||
# Does the node exist yet? | |||||
if not os.path.isfile(str(node)): | |||||
return [] | |||||
if env.get('DOCBOOK_SCANENT',''): | |||||
# Use simple pattern matching for system entities..., no support | |||||
# for recursion yet. | |||||
contents = node.get_text_contents() | |||||
return sentity_re.findall(contents) | |||||
xsl_file = os.path.join(scriptpath,'utils','xmldepend.xsl') | |||||
if not has_libxml2 or prefer_xsltproc: | |||||
if has_lxml and not prefer_xsltproc: | |||||
from lxml import etree | |||||
xsl_tree = etree.parse(xsl_file) | |||||
doc = etree.parse(str(node)) | |||||
result = doc.xslt(xsl_tree) | |||||
depfiles = [x.strip() for x in str(result).splitlines() if x.strip() != "" and not x.startswith("<?xml ")] | |||||
return depfiles | |||||
else: | |||||
# Try to call xsltproc | |||||
xsltproc = env.subst("$DOCBOOK_XSLTPROC") | |||||
if xsltproc and xsltproc.endswith('xsltproc'): | |||||
result = env.backtick(' '.join([xsltproc, xsl_file, str(node)])) | |||||
depfiles = [x.strip() for x in str(result).splitlines() if x.strip() != "" and not x.startswith("<?xml ")] | |||||
return depfiles | |||||
else: | |||||
# Use simple pattern matching, there is currently no support | |||||
# for xi:includes... | |||||
contents = node.get_text_contents() | |||||
return include_re.findall(contents) | |||||
styledoc = libxml2.parseFile(xsl_file) | |||||
style = libxslt.parseStylesheetDoc(styledoc) | |||||
doc = libxml2.readFile(str(node), None, libxml2.XML_PARSE_NOENT) | |||||
result = style.applyStylesheet(doc, None) | |||||
depfiles = [] | |||||
for x in str(result).splitlines(): | |||||
if x.strip() != "" and not x.startswith("<?xml "): | |||||
depfiles.extend(x.strip().split()) | |||||
style.freeStylesheet() | |||||
doc.freeDoc() | |||||
result.freeDoc() | |||||
return depfiles | |||||
# Creating the instance of our XML dependency scanner | |||||
docbook_xml_scanner = SCons.Script.Scanner(function = __xml_scan, | |||||
argument = None) | |||||
# | |||||
# Action generators | |||||
# | |||||
def __generate_xsltproc_action(source, target, env, for_signature): | |||||
cmd = env['DOCBOOK_XSLTPROCCOM'] | |||||
# Does the environment have a base_dir defined? | |||||
base_dir = env.subst('$base_dir') | |||||
if base_dir: | |||||
# Yes, so replace target path by its filename | |||||
return cmd.replace('$TARGET','${TARGET.file}') | |||||
return cmd | |||||
# | |||||
# Emitters | |||||
# | |||||
def __emit_xsl_basedir(target, source, env): | |||||
# Does the environment have a base_dir defined? | |||||
base_dir = env.subst('$base_dir') | |||||
if base_dir: | |||||
# Yes, so prepend it to each target | |||||
return [os.path.join(base_dir, str(t)) for t in target], source | |||||
# No, so simply pass target and source names through | |||||
return target, source | |||||
# | |||||
# Builders | |||||
# | |||||
def __build_libxml2(target, source, env): | |||||
""" | |||||
General XSLT builder (HTML/FO), using the libxml2 module. | |||||
""" | |||||
xsl_style = env.subst('$DOCBOOK_XSL') | |||||
styledoc = libxml2.parseFile(xsl_style) | |||||
style = libxslt.parseStylesheetDoc(styledoc) | |||||
doc = libxml2.readFile(str(source[0]),None,libxml2.XML_PARSE_NOENT) | |||||
# Support for additional parameters | |||||
parampass = {} | |||||
if parampass: | |||||
result = style.applyStylesheet(doc, parampass) | |||||
else: | |||||
result = style.applyStylesheet(doc, None) | |||||
style.saveResultToFilename(str(target[0]), result, 0) | |||||
style.freeStylesheet() | |||||
doc.freeDoc() | |||||
result.freeDoc() | |||||
return None | |||||
def __build_lxml(target, source, env): | |||||
""" | |||||
General XSLT builder (HTML/FO), using the lxml module. | |||||
""" | |||||
from lxml import etree | |||||
xslt_ac = etree.XSLTAccessControl(read_file=True, | |||||
write_file=True, | |||||
create_dir=True, | |||||
read_network=False, | |||||
write_network=False) | |||||
xsl_style = env.subst('$DOCBOOK_XSL') | |||||
xsl_tree = etree.parse(xsl_style) | |||||
transform = etree.XSLT(xsl_tree, access_control=xslt_ac) | |||||
doc = etree.parse(str(source[0])) | |||||
# Support for additional parameters | |||||
parampass = {} | |||||
if parampass: | |||||
result = transform(doc, **parampass) | |||||
else: | |||||
result = transform(doc) | |||||
try: | |||||
of = open(str(target[0]), "wb") | |||||
of.write(of.write(etree.tostring(result, pretty_print=True))) | |||||
of.close() | |||||
except: | |||||
pass | |||||
return None | |||||
def __xinclude_libxml2(target, source, env): | |||||
""" | |||||
Resolving XIncludes, using the libxml2 module. | |||||
""" | |||||
doc = libxml2.readFile(str(source[0]), None, libxml2.XML_PARSE_NOENT) | |||||
doc.xincludeProcessFlags(libxml2.XML_PARSE_NOENT) | |||||
doc.saveFile(str(target[0])) | |||||
doc.freeDoc() | |||||
return None | |||||
def __xinclude_lxml(target, source, env): | |||||
""" | |||||
Resolving XIncludes, using the lxml module. | |||||
""" | |||||
from lxml import etree | |||||
doc = etree.parse(str(source[0])) | |||||
doc.xinclude() | |||||
try: | |||||
doc.write(str(target[0]), xml_declaration=True, | |||||
encoding="UTF-8", pretty_print=True) | |||||
except: | |||||
pass | |||||
return None | |||||
__libxml2_builder = SCons.Builder.Builder( | |||||
action = __build_libxml2, | |||||
src_suffix = '.xml', | |||||
source_scanner = docbook_xml_scanner, | |||||
emitter = __emit_xsl_basedir) | |||||
__lxml_builder = SCons.Builder.Builder( | |||||
action = __build_lxml, | |||||
src_suffix = '.xml', | |||||
source_scanner = docbook_xml_scanner, | |||||
emitter = __emit_xsl_basedir) | |||||
__xinclude_libxml2_builder = SCons.Builder.Builder( | |||||
action = __xinclude_libxml2, | |||||
suffix = '.xml', | |||||
src_suffix = '.xml', | |||||
source_scanner = docbook_xml_scanner) | |||||
__xinclude_lxml_builder = SCons.Builder.Builder( | |||||
action = __xinclude_lxml, | |||||
suffix = '.xml', | |||||
src_suffix = '.xml', | |||||
source_scanner = docbook_xml_scanner) | |||||
__xsltproc_builder = SCons.Builder.Builder( | |||||
action = SCons.Action.CommandGeneratorAction(__generate_xsltproc_action, | |||||
{'cmdstr' : '$DOCBOOK_XSLTPROCCOMSTR'}), | |||||
src_suffix = '.xml', | |||||
source_scanner = docbook_xml_scanner, | |||||
emitter = __emit_xsl_basedir) | |||||
__xmllint_builder = SCons.Builder.Builder( | |||||
action = SCons.Action.Action('$DOCBOOK_XMLLINTCOM','$DOCBOOK_XMLLINTCOMSTR'), | |||||
suffix = '.xml', | |||||
src_suffix = '.xml', | |||||
source_scanner = docbook_xml_scanner) | |||||
__fop_builder = SCons.Builder.Builder( | |||||
action = SCons.Action.Action('$DOCBOOK_FOPCOM','$DOCBOOK_FOPCOMSTR'), | |||||
suffix = '.pdf', | |||||
src_suffix = '.fo', | |||||
ensure_suffix=1) | |||||
def DocbookEpub(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for ePub output. | |||||
""" | |||||
import zipfile | |||||
import shutil | |||||
def build_open_container(target, source, env): | |||||
"""Generate the *.epub file from intermediate outputs | |||||
Constructs the epub file according to the Open Container Format. This | |||||
function could be replaced by a call to the SCons Zip builder if support | |||||
was added for different compression formats for separate source nodes. | |||||
""" | |||||
zf = zipfile.ZipFile(str(target[0]), 'w') | |||||
mime_file = open('mimetype', 'w') | |||||
mime_file.write('application/epub+zip') | |||||
mime_file.close() | |||||
zf.write(mime_file.name, compress_type = zipfile.ZIP_STORED) | |||||
for s in source: | |||||
if os.path.isfile(str(s)): | |||||
head, tail = os.path.split(str(s)) | |||||
if not head: | |||||
continue | |||||
s = head | |||||
for dirpath, dirnames, filenames in os.walk(str(s)): | |||||
for fname in filenames: | |||||
path = os.path.join(dirpath, fname) | |||||
if os.path.isfile(path): | |||||
zf.write(path, os.path.relpath(path, str(env.get('ZIPROOT', ''))), | |||||
zipfile.ZIP_DEFLATED) | |||||
zf.close() | |||||
def add_resources(target, source, env): | |||||
"""Add missing resources to the OEBPS directory | |||||
Ensure all the resources in the manifest are present in the OEBPS directory. | |||||
""" | |||||
hrefs = [] | |||||
content_file = os.path.join(source[0].get_abspath(), 'content.opf') | |||||
if not os.path.isfile(content_file): | |||||
return | |||||
hrefs = [] | |||||
if has_libxml2: | |||||
nsmap = {'opf' : 'http://www.idpf.org/2007/opf'} | |||||
# Read file and resolve entities | |||||
doc = libxml2.readFile(content_file, None, 0) | |||||
opf = doc.getRootElement() | |||||
# Create xpath context | |||||
xpath_context = doc.xpathNewContext() | |||||
# Register namespaces | |||||
for key, val in nsmap.items(): | |||||
xpath_context.xpathRegisterNs(key, val) | |||||
if hasattr(opf, 'xpathEval') and xpath_context: | |||||
# Use the xpath context | |||||
xpath_context.setContextNode(opf) | |||||
items = xpath_context.xpathEval(".//opf:item") | |||||
else: | |||||
items = opf.findall(".//{'http://www.idpf.org/2007/opf'}item") | |||||
for item in items: | |||||
if hasattr(item, 'prop'): | |||||
hrefs.append(item.prop('href')) | |||||
else: | |||||
hrefs.append(item.attrib['href']) | |||||
doc.freeDoc() | |||||
xpath_context.xpathFreeContext() | |||||
elif has_lxml: | |||||
from lxml import etree | |||||
opf = etree.parse(content_file) | |||||
# All the opf:item elements are resources | |||||
for item in opf.xpath('//opf:item', | |||||
namespaces= { 'opf': 'http://www.idpf.org/2007/opf' }): | |||||
hrefs.append(item.attrib['href']) | |||||
for href in hrefs: | |||||
# If the resource was not already created by DocBook XSL itself, | |||||
# copy it into the OEBPS folder | |||||
referenced_file = os.path.join(source[0].get_abspath(), href) | |||||
if not os.path.exists(referenced_file): | |||||
shutil.copy(href, os.path.join(source[0].get_abspath(), href)) | |||||
# Init list of targets/sources | |||||
target, source = __extend_targets_sources(target, source) | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_EPUB', ['epub','docbook.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Create targets | |||||
result = [] | |||||
if not env.GetOption('clean'): | |||||
# Ensure that the folders OEBPS and META-INF exist | |||||
__create_output_dir('OEBPS/') | |||||
__create_output_dir('META-INF/') | |||||
dirs = env.Dir(['OEBPS', 'META-INF']) | |||||
# Set the fixed base_dir | |||||
kw['base_dir'] = 'OEBPS/' | |||||
tocncx = __builder.__call__(env, 'toc.ncx', source[0], **kw) | |||||
cxml = env.File('META-INF/container.xml') | |||||
env.SideEffect(cxml, tocncx) | |||||
env.Depends(tocncx, kw['DOCBOOK_XSL']) | |||||
result.extend(tocncx+[cxml]) | |||||
container = env.Command(__ensure_suffix(str(target[0]), '.epub'), | |||||
tocncx+[cxml], [add_resources, build_open_container]) | |||||
mimetype = env.File('mimetype') | |||||
env.SideEffect(mimetype, container) | |||||
result.extend(container) | |||||
# Add supporting files for cleanup | |||||
env.Clean(tocncx, dirs) | |||||
return result | |||||
def DocbookHtml(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for HTML output. | |||||
""" | |||||
# Init list of targets/sources | |||||
target, source = __extend_targets_sources(target, source) | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_HTML', ['html','docbook.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Create targets | |||||
result = [] | |||||
for t,s in zip(target,source): | |||||
r = __builder.__call__(env, __ensure_suffix(t,'.html'), s, **kw) | |||||
env.Depends(r, kw['DOCBOOK_XSL']) | |||||
result.extend(r) | |||||
return result | |||||
def DocbookHtmlChunked(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for chunked HTML output. | |||||
""" | |||||
# Init target/source | |||||
if not SCons.Util.is_List(target): | |||||
target = [target] | |||||
if not source: | |||||
source = target | |||||
target = ['index.html'] | |||||
elif not SCons.Util.is_List(source): | |||||
source = [source] | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_HTMLCHUNKED', ['html','chunkfast.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Detect base dir | |||||
base_dir = kw.get('base_dir', '') | |||||
if base_dir: | |||||
__create_output_dir(base_dir) | |||||
# Create targets | |||||
result = [] | |||||
r = __builder.__call__(env, __ensure_suffix(str(target[0]), '.html'), source[0], **kw) | |||||
env.Depends(r, kw['DOCBOOK_XSL']) | |||||
result.extend(r) | |||||
# Add supporting files for cleanup | |||||
env.Clean(r, glob.glob(os.path.join(base_dir, '*.html'))) | |||||
return result | |||||
def DocbookHtmlhelp(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for HTMLHELP output. | |||||
""" | |||||
# Init target/source | |||||
if not SCons.Util.is_List(target): | |||||
target = [target] | |||||
if not source: | |||||
source = target | |||||
target = ['index.html'] | |||||
elif not SCons.Util.is_List(source): | |||||
source = [source] | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_HTMLHELP', ['htmlhelp','htmlhelp.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Detect base dir | |||||
base_dir = kw.get('base_dir', '') | |||||
if base_dir: | |||||
__create_output_dir(base_dir) | |||||
# Create targets | |||||
result = [] | |||||
r = __builder.__call__(env, __ensure_suffix(str(target[0]), '.html'), source[0], **kw) | |||||
env.Depends(r, kw['DOCBOOK_XSL']) | |||||
result.extend(r) | |||||
# Add supporting files for cleanup | |||||
env.Clean(r, ['toc.hhc', 'htmlhelp.hhp', 'index.hhk'] + | |||||
glob.glob(os.path.join(base_dir, '[ar|bk|ch]*.html'))) | |||||
return result | |||||
def DocbookPdf(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for PDF output. | |||||
""" | |||||
# Init list of targets/sources | |||||
target, source = __extend_targets_sources(target, source) | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_PDF', ['fo','docbook.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Create targets | |||||
result = [] | |||||
for t,s in zip(target,source): | |||||
t, stem = __ensure_suffix_stem(t, '.pdf') | |||||
xsl = __builder.__call__(env, stem+'.fo', s, **kw) | |||||
result.extend(xsl) | |||||
env.Depends(xsl, kw['DOCBOOK_XSL']) | |||||
result.extend(__fop_builder.__call__(env, t, xsl, **kw)) | |||||
return result | |||||
def DocbookMan(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for Man page output. | |||||
""" | |||||
# Init list of targets/sources | |||||
target, source = __extend_targets_sources(target, source) | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_MAN', ['manpages','docbook.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Create targets | |||||
result = [] | |||||
for t,s in zip(target,source): | |||||
volnum = "1" | |||||
outfiles = [] | |||||
srcfile = __ensure_suffix(str(s),'.xml') | |||||
if os.path.isfile(srcfile): | |||||
try: | |||||
import xml.dom.minidom | |||||
dom = xml.dom.minidom.parse(__ensure_suffix(str(s),'.xml')) | |||||
# Extract volume number, default is 1 | |||||
for node in dom.getElementsByTagName('refmeta'): | |||||
for vol in node.getElementsByTagName('manvolnum'): | |||||
volnum = __get_xml_text(vol) | |||||
# Extract output filenames | |||||
for node in dom.getElementsByTagName('refnamediv'): | |||||
for ref in node.getElementsByTagName('refname'): | |||||
outfiles.append(__get_xml_text(ref)+'.'+volnum) | |||||
except: | |||||
# Use simple regex parsing | |||||
f = open(__ensure_suffix(str(s),'.xml'), 'r') | |||||
content = f.read() | |||||
f.close() | |||||
for m in re_manvolnum.finditer(content): | |||||
volnum = m.group(1) | |||||
for m in re_refname.finditer(content): | |||||
outfiles.append(m.group(1)+'.'+volnum) | |||||
if not outfiles: | |||||
# Use stem of the source file | |||||
spath = str(s) | |||||
if not spath.endswith('.xml'): | |||||
outfiles.append(spath+'.'+volnum) | |||||
else: | |||||
stem, ext = os.path.splitext(spath) | |||||
outfiles.append(stem+'.'+volnum) | |||||
else: | |||||
# We have to completely rely on the given target name | |||||
outfiles.append(t) | |||||
__builder.__call__(env, outfiles[0], s, **kw) | |||||
env.Depends(outfiles[0], kw['DOCBOOK_XSL']) | |||||
result.append(outfiles[0]) | |||||
if len(outfiles) > 1: | |||||
env.Clean(outfiles[0], outfiles[1:]) | |||||
return result | |||||
def DocbookSlidesPdf(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for PDF slides output. | |||||
""" | |||||
# Init list of targets/sources | |||||
target, source = __extend_targets_sources(target, source) | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_SLIDESPDF', ['slides','fo','plain.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Create targets | |||||
result = [] | |||||
for t,s in zip(target,source): | |||||
t, stem = __ensure_suffix_stem(t, '.pdf') | |||||
xsl = __builder.__call__(env, stem+'.fo', s, **kw) | |||||
env.Depends(xsl, kw['DOCBOOK_XSL']) | |||||
result.extend(xsl) | |||||
result.extend(__fop_builder.__call__(env, t, xsl, **kw)) | |||||
return result | |||||
def DocbookSlidesHtml(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, providing a Docbook toolchain for HTML slides output. | |||||
""" | |||||
# Init list of targets/sources | |||||
if not SCons.Util.is_List(target): | |||||
target = [target] | |||||
if not source: | |||||
source = target | |||||
target = ['index.html'] | |||||
elif not SCons.Util.is_List(source): | |||||
source = [source] | |||||
# Init XSL stylesheet | |||||
__init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_SLIDESHTML', ['slides','html','plain.xsl']) | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Detect base dir | |||||
base_dir = kw.get('base_dir', '') | |||||
if base_dir: | |||||
__create_output_dir(base_dir) | |||||
# Create targets | |||||
result = [] | |||||
r = __builder.__call__(env, __ensure_suffix(str(target[0]), '.html'), source[0], **kw) | |||||
env.Depends(r, kw['DOCBOOK_XSL']) | |||||
result.extend(r) | |||||
# Add supporting files for cleanup | |||||
env.Clean(r, [os.path.join(base_dir, 'toc.html')] + | |||||
glob.glob(os.path.join(base_dir, 'foil*.html'))) | |||||
return result | |||||
def DocbookXInclude(env, target, source, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, for resolving XIncludes in a separate processing step. | |||||
""" | |||||
# Init list of targets/sources | |||||
target, source = __extend_targets_sources(target, source) | |||||
# Setup builder | |||||
__builder = __select_builder(__xinclude_lxml_builder,__xinclude_libxml2_builder,__xmllint_builder) | |||||
# Create targets | |||||
result = [] | |||||
for t,s in zip(target,source): | |||||
result.extend(__builder.__call__(env, t, s, **kw)) | |||||
return result | |||||
def DocbookXslt(env, target, source=None, *args, **kw): | |||||
""" | |||||
A pseudo-Builder, applying a simple XSL transformation to the input file. | |||||
""" | |||||
# Init list of targets/sources | |||||
target, source = __extend_targets_sources(target, source) | |||||
# Init XSL stylesheet | |||||
kw['DOCBOOK_XSL'] = kw.get('xsl', 'transform.xsl') | |||||
# Setup builder | |||||
__builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) | |||||
# Create targets | |||||
result = [] | |||||
for t,s in zip(target,source): | |||||
r = __builder.__call__(env, t, s, **kw) | |||||
env.Depends(r, kw['DOCBOOK_XSL']) | |||||
result.extend(r) | |||||
return result | |||||
def generate(env): | |||||
"""Add Builders and construction variables for docbook to an Environment.""" | |||||
env.SetDefault( | |||||
# Default names for customized XSL stylesheets | |||||
DOCBOOK_DEFAULT_XSL_EPUB = '', | |||||
DOCBOOK_DEFAULT_XSL_HTML = '', | |||||
DOCBOOK_DEFAULT_XSL_HTMLCHUNKED = '', | |||||
DOCBOOK_DEFAULT_XSL_HTMLHELP = '', | |||||
DOCBOOK_DEFAULT_XSL_PDF = '', | |||||
DOCBOOK_DEFAULT_XSL_MAN = '', | |||||
DOCBOOK_DEFAULT_XSL_SLIDESPDF = '', | |||||
DOCBOOK_DEFAULT_XSL_SLIDESHTML = '', | |||||
# Paths to the detected executables | |||||
DOCBOOK_XSLTPROC = '', | |||||
DOCBOOK_XMLLINT = '', | |||||
DOCBOOK_FOP = '', | |||||
# Additional flags for the text processors | |||||
DOCBOOK_XSLTPROCFLAGS = SCons.Util.CLVar(''), | |||||
DOCBOOK_XMLLINTFLAGS = SCons.Util.CLVar(''), | |||||
DOCBOOK_FOPFLAGS = SCons.Util.CLVar(''), | |||||
DOCBOOK_XSLTPROCPARAMS = SCons.Util.CLVar(''), | |||||
# Default command lines for the detected executables | |||||
DOCBOOK_XSLTPROCCOM = xsltproc_com['xsltproc'], | |||||
DOCBOOK_XMLLINTCOM = xmllint_com['xmllint'], | |||||
DOCBOOK_FOPCOM = fop_com['fop'], | |||||
# Screen output for the text processors | |||||
DOCBOOK_XSLTPROCCOMSTR = None, | |||||
DOCBOOK_XMLLINTCOMSTR = None, | |||||
DOCBOOK_FOPCOMSTR = None, | |||||
) | |||||
_detect(env) | |||||
env.AddMethod(DocbookEpub, "DocbookEpub") | |||||
env.AddMethod(DocbookHtml, "DocbookHtml") | |||||
env.AddMethod(DocbookHtmlChunked, "DocbookHtmlChunked") | |||||
env.AddMethod(DocbookHtmlhelp, "DocbookHtmlhelp") | |||||
env.AddMethod(DocbookPdf, "DocbookPdf") | |||||
env.AddMethod(DocbookMan, "DocbookMan") | |||||
env.AddMethod(DocbookSlidesPdf, "DocbookSlidesPdf") | |||||
env.AddMethod(DocbookSlidesHtml, "DocbookSlidesHtml") | |||||
env.AddMethod(DocbookXInclude, "DocbookXInclude") | |||||
env.AddMethod(DocbookXslt, "DocbookXslt") | |||||
def exists(env): | |||||
return 1 |
@ -0,0 +1,64 @@ | |||||
"""SCons.Tool.dvi | |||||
Common DVI Builder definition for various other Tool modules that use it. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/dvi.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Builder | |||||
import SCons.Tool | |||||
DVIBuilder = None | |||||
def generate(env): | |||||
try: | |||||
env['BUILDERS']['DVI'] | |||||
except KeyError: | |||||
global DVIBuilder | |||||
if DVIBuilder is None: | |||||
# The suffix is hard-coded to '.dvi', not configurable via a | |||||
# construction variable like $DVISUFFIX, because the output | |||||
# file name is hard-coded within TeX. | |||||
DVIBuilder = SCons.Builder.Builder(action = {}, | |||||
source_scanner = SCons.Tool.LaTeXScanner, | |||||
suffix = '.dvi', | |||||
emitter = {}, | |||||
source_ext_match = None) | |||||
env['BUILDERS']['DVI'] = DVIBuilder | |||||
def exists(env): | |||||
# This only puts a skeleton Builder in place, so if someone | |||||
# references this Tool directly, it's always "available." | |||||
return 1 | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,125 @@ | |||||
"""SCons.Tool.dvipdf | |||||
Tool-specific initialization for dvipdf. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
__revision__ = "src/engine/SCons/Tool/dvipdf.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Action | |||||
import SCons.Defaults | |||||
import SCons.Tool.pdf | |||||
import SCons.Tool.tex | |||||
import SCons.Util | |||||
_null = SCons.Scanner.LaTeX._null | |||||
def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None): | |||||
"""A builder for DVI files that sets the TEXPICTS environment | |||||
variable before running dvi2ps or dvipdf.""" | |||||
try: | |||||
abspath = source[0].attributes.path | |||||
except AttributeError : | |||||
abspath = '' | |||||
saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath) | |||||
result = XXXDviAction(target, source, env) | |||||
if saved_env is _null: | |||||
try: | |||||
del env['ENV']['TEXPICTS'] | |||||
except KeyError: | |||||
pass # was never set | |||||
else: | |||||
env['ENV']['TEXPICTS'] = saved_env | |||||
return result | |||||
def DviPdfFunction(target = None, source= None, env=None): | |||||
result = DviPdfPsFunction(PDFAction,target,source,env) | |||||
return result | |||||
def DviPdfStrFunction(target = None, source= None, env=None): | |||||
"""A strfunction for dvipdf that returns the appropriate | |||||
command string for the no_exec options.""" | |||||
if env.GetOption("no_exec"): | |||||
result = env.subst('$DVIPDFCOM',0,target,source) | |||||
else: | |||||
result = '' | |||||
return result | |||||
PDFAction = None | |||||
DVIPDFAction = None | |||||
def PDFEmitter(target, source, env): | |||||
"""Strips any .aux or .log files from the input source list. | |||||
These are created by the TeX Builder that in all likelihood was | |||||
used to generate the .dvi file we're using as input, and we only | |||||
care about the .dvi file. | |||||
""" | |||||
def strip_suffixes(n): | |||||
return not SCons.Util.splitext(str(n))[1] in ['.aux', '.log'] | |||||
source = [src for src in source if strip_suffixes(src)] | |||||
return (target, source) | |||||
def generate(env): | |||||
"""Add Builders and construction variables for dvipdf to an Environment.""" | |||||
global PDFAction | |||||
if PDFAction is None: | |||||
PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR') | |||||
global DVIPDFAction | |||||
if DVIPDFAction is None: | |||||
DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction) | |||||
from . import pdf | |||||
pdf.generate(env) | |||||
bld = env['BUILDERS']['PDF'] | |||||
bld.add_action('.dvi', DVIPDFAction) | |||||
bld.add_emitter('.dvi', PDFEmitter) | |||||
env['DVIPDF'] = 'dvipdf' | |||||
env['DVIPDFFLAGS'] = SCons.Util.CLVar('') | |||||
env['DVIPDFCOM'] = 'cd ${TARGET.dir} && $DVIPDF $DVIPDFFLAGS ${SOURCE.file} ${TARGET.file}' | |||||
# Deprecated synonym. | |||||
env['PDFCOM'] = ['$DVIPDFCOM'] | |||||
def exists(env): | |||||
SCons.Tool.tex.generate_darwin(env) | |||||
return env.Detect('dvipdf') | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,95 @@ | |||||
"""SCons.Tool.dvips | |||||
Tool-specific initialization for dvips. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/dvips.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Action | |||||
import SCons.Builder | |||||
import SCons.Tool.dvipdf | |||||
import SCons.Util | |||||
def DviPsFunction(target = None, source= None, env=None): | |||||
result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env) | |||||
return result | |||||
def DviPsStrFunction(target = None, source= None, env=None): | |||||
"""A strfunction for dvipdf that returns the appropriate | |||||
command string for the no_exec options.""" | |||||
if env.GetOption("no_exec"): | |||||
result = env.subst('$PSCOM',0,target,source) | |||||
else: | |||||
result = '' | |||||
return result | |||||
PSAction = None | |||||
DVIPSAction = None | |||||
PSBuilder = None | |||||
def generate(env): | |||||
"""Add Builders and construction variables for dvips to an Environment.""" | |||||
global PSAction | |||||
if PSAction is None: | |||||
PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR') | |||||
global DVIPSAction | |||||
if DVIPSAction is None: | |||||
DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction) | |||||
global PSBuilder | |||||
if PSBuilder is None: | |||||
PSBuilder = SCons.Builder.Builder(action = PSAction, | |||||
prefix = '$PSPREFIX', | |||||
suffix = '$PSSUFFIX', | |||||
src_suffix = '.dvi', | |||||
src_builder = 'DVI', | |||||
single_source=True) | |||||
env['BUILDERS']['PostScript'] = PSBuilder | |||||
env['DVIPS'] = 'dvips' | |||||
env['DVIPSFLAGS'] = SCons.Util.CLVar('') | |||||
# I'm not quite sure I got the directories and filenames right for variant_dir | |||||
# We need to be in the correct directory for the sake of latex \includegraphics eps included files. | |||||
env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}' | |||||
env['PSPREFIX'] = '' | |||||
env['PSSUFFIX'] = '.ps' | |||||
def exists(env): | |||||
SCons.Tool.tex.generate_darwin(env) | |||||
return env.Detect('dvips') | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,63 @@ | |||||
"""engine.SCons.Tool.f03 | |||||
Tool-specific initialization for the generic Posix f03 Fortran compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/f03.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Defaults | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
from . import fortran | |||||
from SCons.Tool.FortranCommon import add_all_to_env, add_f03_to_env | |||||
compilers = ['f03'] | |||||
def generate(env): | |||||
add_all_to_env(env) | |||||
add_f03_to_env(env) | |||||
fcomp = env.Detect(compilers) or 'f03' | |||||
env['F03'] = fcomp | |||||
env['SHF03'] = fcomp | |||||
env['FORTRAN'] = fcomp | |||||
env['SHFORTRAN'] = fcomp | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,65 @@ | |||||
"""engine.SCons.Tool.f08 | |||||
Tool-specific initialization for the generic Posix f08 Fortran compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
from __future__ import absolute_import | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/f08.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Defaults | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
from . import fortran | |||||
from SCons.Tool.FortranCommon import add_all_to_env, add_f08_to_env | |||||
compilers = ['f08'] | |||||
def generate(env): | |||||
add_all_to_env(env) | |||||
add_f08_to_env(env) | |||||
fcomp = env.Detect(compilers) or 'f08' | |||||
env['F08'] = fcomp | |||||
env['SHF08'] = fcomp | |||||
env['FORTRAN'] = fcomp | |||||
env['SHFORTRAN'] = fcomp | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,62 @@ | |||||
"""engine.SCons.Tool.f77 | |||||
Tool-specific initialization for the generic Posix f77 Fortran compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/f77.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Defaults | |||||
import SCons.Scanner.Fortran | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env | |||||
compilers = ['f77'] | |||||
def generate(env): | |||||
add_all_to_env(env) | |||||
add_f77_to_env(env) | |||||
fcomp = env.Detect(compilers) or 'f77' | |||||
env['F77'] = fcomp | |||||
env['SHF77'] = fcomp | |||||
env['FORTRAN'] = fcomp | |||||
env['SHFORTRAN'] = fcomp | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,62 @@ | |||||
"""engine.SCons.Tool.f90 | |||||
Tool-specific initialization for the generic Posix f90 Fortran compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/f90.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Defaults | |||||
import SCons.Scanner.Fortran | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env | |||||
compilers = ['f90'] | |||||
def generate(env): | |||||
add_all_to_env(env) | |||||
add_f90_to_env(env) | |||||
fc = env.Detect(compilers) or 'f90' | |||||
env['F90'] = fc | |||||
env['SHF90'] = fc | |||||
env['FORTRAN'] = fc | |||||
env['SHFORTRAN'] = fc | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,63 @@ | |||||
"""engine.SCons.Tool.f95 | |||||
Tool-specific initialization for the generic Posix f95 Fortran compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/f95.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Defaults | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
from . import fortran | |||||
from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env | |||||
compilers = ['f95'] | |||||
def generate(env): | |||||
add_all_to_env(env) | |||||
add_f95_to_env(env) | |||||
fcomp = env.Detect(compilers) or 'f95' | |||||
env['F95'] = fcomp | |||||
env['SHF95'] = fcomp | |||||
env['FORTRAN'] = fcomp | |||||
env['SHFORTRAN'] = fcomp | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,98 @@ | |||||
"""SCons.Tool.filesystem | |||||
Tool-specific initialization for the filesystem tools. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/filesystem.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons | |||||
from SCons.Tool.install import copyFunc | |||||
copyToBuilder, copyAsBuilder = None, None | |||||
def copyto_emitter(target, source, env): | |||||
""" changes the path of the source to be under the target (which | |||||
are assumed to be directories. | |||||
""" | |||||
n_target = [] | |||||
for t in target: | |||||
n_target = n_target + [t.File( str( s ) ) for s in source] | |||||
return (n_target, source) | |||||
def copy_action_func(target, source, env): | |||||
assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(list(map(str, target)),list(map(str, source))) | |||||
for t, s in zip(target, source): | |||||
if copyFunc(t.get_path(), s.get_path(), env): | |||||
return 1 | |||||
return 0 | |||||
def copy_action_str(target, source, env): | |||||
return env.subst_target_source(env['COPYSTR'], 0, target, source) | |||||
copy_action = SCons.Action.Action( copy_action_func, copy_action_str ) | |||||
def generate(env): | |||||
try: | |||||
env['BUILDERS']['CopyTo'] | |||||
env['BUILDERS']['CopyAs'] | |||||
except KeyError as e: | |||||
global copyToBuilder | |||||
if copyToBuilder is None: | |||||
copyToBuilder = SCons.Builder.Builder( | |||||
action = copy_action, | |||||
target_factory = env.fs.Dir, | |||||
source_factory = env.fs.Entry, | |||||
multi = 1, | |||||
emitter = [ copyto_emitter, ] ) | |||||
global copyAsBuilder | |||||
if copyAsBuilder is None: | |||||
copyAsBuilder = SCons.Builder.Builder( | |||||
action = copy_action, | |||||
target_factory = env.fs.Entry, | |||||
source_factory = env.fs.Entry ) | |||||
env['BUILDERS']['CopyTo'] = copyToBuilder | |||||
env['BUILDERS']['CopyAs'] = copyAsBuilder | |||||
env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"' | |||||
def exists(env): | |||||
return 1 | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,62 @@ | |||||
"""SCons.Tool.fortran | |||||
Tool-specific initialization for a generic Posix f77/f90 Fortran compiler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/fortran.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import re | |||||
import SCons.Action | |||||
import SCons.Defaults | |||||
import SCons.Scanner.Fortran | |||||
import SCons.Tool | |||||
import SCons.Util | |||||
from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env | |||||
compilers = ['f95', 'f90', 'f77'] | |||||
def generate(env): | |||||
add_all_to_env(env) | |||||
add_fortran_to_env(env) | |||||
fc = env.Detect(compilers) or 'f77' | |||||
env['SHFORTRAN'] = fc | |||||
env['FORTRAN'] = fc | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,45 @@ | |||||
"""SCons.Tool.g++ | |||||
Tool-specific initialization for g++. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/g++.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
#forward proxy to the preffered cxx version | |||||
from SCons.Tool.gxx import * | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,73 @@ | |||||
"""engine.SCons.Tool.g77 | |||||
Tool-specific initialization for g77. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/g77.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
import SCons.Util | |||||
from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env | |||||
compilers = ['g77', 'f77'] | |||||
def generate(env): | |||||
"""Add Builders and construction variables for g77 to an Environment.""" | |||||
add_all_to_env(env) | |||||
add_f77_to_env(env) | |||||
fcomp = env.Detect(compilers) or 'g77' | |||||
if env['PLATFORM'] in ['cygwin', 'win32']: | |||||
env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS') | |||||
env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') | |||||
else: | |||||
env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC') | |||||
env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC') | |||||
env['FORTRAN'] = fcomp | |||||
env['SHFORTRAN'] = '$FORTRAN' | |||||
env['F77'] = fcomp | |||||
env['SHF77'] = '$F77' | |||||
env['INCFORTRANPREFIX'] = "-I" | |||||
env['INCFORTRANSUFFIX'] = "" | |||||
env['INCF77PREFIX'] = "-I" | |||||
env['INCF77SUFFIX'] = "" | |||||
def exists(env): | |||||
return env.Detect(compilers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |
@ -0,0 +1,56 @@ | |||||
"""SCons.Tool.gas | |||||
Tool-specific initialization for as, the Gnu assembler. | |||||
There normally shouldn't be any need to import this module directly. | |||||
It will usually be imported through the generic SCons.Tool.Tool() | |||||
selection method. | |||||
""" | |||||
# | |||||
# Copyright (c) 2001 - 2017 The SCons Foundation | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining | |||||
# a copy of this software and associated documentation files (the | |||||
# "Software"), to deal in the Software without restriction, including | |||||
# without limitation the rights to use, copy, modify, merge, publish, | |||||
# distribute, sublicense, and/or sell copies of the Software, and to | |||||
# permit persons to whom the Software is furnished to do so, subject to | |||||
# the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included | |||||
# in all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | |||||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |||||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
__revision__ = "src/engine/SCons/Tool/gas.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" | |||||
try: | |||||
as_module = __import__('as', globals(), locals(), []) | |||||
except: | |||||
as_module = __import__(__package__+'.as', globals(), locals(), ['*']) | |||||
assemblers = ['as', 'gas'] | |||||
def generate(env): | |||||
"""Add Builders and construction variables for as to an Environment.""" | |||||
as_module.generate(env) | |||||
env['AS'] = env.Detect(assemblers) or 'as' | |||||
def exists(env): | |||||
return env.Detect(assemblers) | |||||
# Local Variables: | |||||
# tab-width:4 | |||||
# indent-tabs-mode:nil | |||||
# End: | |||||
# vim: set expandtab tabstop=4 shiftwidth=4: |