3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """Tool-specific initialization for swig.
26 There normally shouldn't be any need to import this module directly.
27 It will usually be imported through the generic SCons.Tool.Tool()
34 from subprocess
import PIPE
45 swigs
= [ 'swig', 'swig3.0', 'swig2.0' ]
47 SwigAction
= SCons
.Action
.Action('$SWIGCOM', '$SWIGCOMSTR')
49 def swigSuffixEmitter(env
, source
) -> str:
50 if '-c++' in SCons
.Util
.CLVar(env
.subst("$SWIGFLAGS", source
=source
)):
51 return '$SWIGCXXFILESUFFIX'
53 return '$SWIGCFILESUFFIX'
55 # Match '%module test', as well as '%module(directors="1") test'
56 # Also allow for test to be quoted (SWIG permits double quotes, but not single)
57 # Also allow for the line to have spaces after test if not quoted
58 _reModule
= re
.compile(r
'%module(\s*\(.*\))?\s+("?)(\S+)\2')
60 def _find_modules(src
):
61 """Find all modules referenced by %module lines in `src`, a SWIG .i file.
62 Returns a list of all modules, and a flag set if SWIG directors have
63 been requested (SWIG will generate an additional header file in this
70 matches
= _reModule
.findall(data
)
72 # If the file's not yet generated, guess the module name from the file stem
74 mnames
.append(os
.path
.splitext(os
.path
.basename(src
))[0])
78 directors
= directors
or 'directors' in m
[0]
79 return mnames
, directors
81 def _add_director_header_targets(target
, env
) -> None:
82 # Directors only work with C++ code, not C
83 suffix
= env
.subst(env
['SWIGCXXFILESUFFIX'])
84 # For each file ending in SWIGCXXFILESUFFIX, add a new target director
85 # header by replacing the ending with SWIGDIRECTORSUFFIX.
89 if n
[-len(suffix
):] == suffix
:
90 target
.append(d
.File(n
[:-len(suffix
)] + env
['SWIGDIRECTORSUFFIX']))
92 def _swigEmitter(target
, source
, env
):
93 swigflags
= env
.subst("$SWIGFLAGS", target
=target
, source
=source
)
94 flags
= SCons
.Util
.CLVar(swigflags
)
96 src
= str(src
.rfile())
98 if "-python" in flags
and "-noproxy" not in flags
:
100 mnames
, directors
= _find_modules(src
)
102 _add_director_header_targets(target
, env
)
103 python_files
= [m
+ ".py" for m
in mnames
]
104 outdir
= env
.subst('$SWIGOUTDIR', target
=target
, source
=source
)
105 # .py files should be generated in SWIGOUTDIR if specified,
106 # otherwise in the same directory as the target
108 python_files
= [env
.fs
.File(os
.path
.join(outdir
, j
)) for j
in python_files
]
110 python_files
= [target
[0].dir.File(m
) for m
in python_files
]
111 target
.extend(python_files
)
114 mnames
, directors
= _find_modules(src
)
116 _add_director_header_targets(target
, env
)
117 java_files
= [[m
+ ".java", m
+ "JNI.java"] for m
in mnames
]
118 java_files
= SCons
.Util
.flatten(java_files
)
119 outdir
= env
.subst('$SWIGOUTDIR', target
=target
, source
=source
)
121 java_files
= [os
.path
.join(outdir
, j
) for j
in java_files
]
122 java_files
= list(map(env
.fs
.File
, java_files
))
123 def t_from_s(t
, p
, s
, x
):
125 tsm
= SCons
.Node
._target
_from
_source
_map
128 for jf
in java_files
:
129 jf
._func
_target
_from
_source
= tkey
130 target
.extend(java_files
)
131 return (target
, source
)
133 def _get_swig_version(env
, swig
):
134 """Run the SWIG command line tool to get and return the version number"""
136 swig
= env
.subst(swig
)
140 cp
= SCons
.Action
.scons_subproc_run(
141 env
, SCons
.Util
.CLVar(swig
) + ['-version'], stdout
=PIPE
145 out
= SCons
.Util
.to_str(cp
.stdout
)
146 match
= re
.search(r
'SWIG Version\s+(\S+).*', out
, re
.MULTILINE
)
148 version
= match
.group(1)
150 print("Version is: %s" % version
)
153 print("Unable to detect version: [%s]" % out
)
157 def generate(env
) -> None:
158 """Add Builders and construction variables for swig to an Environment."""
159 c_file
, cxx_file
= SCons
.Tool
.createCFileBuilders(env
)
161 c_file
.suffix
['.i'] = swigSuffixEmitter
162 cxx_file
.suffix
['.i'] = swigSuffixEmitter
164 c_file
.add_action('.i', SwigAction
)
165 c_file
.add_emitter('.i', _swigEmitter
)
166 cxx_file
.add_action('.i', SwigAction
)
167 cxx_file
.add_emitter('.i', _swigEmitter
)
169 java_file
= SCons
.Tool
.CreateJavaFileBuilder(env
)
171 java_file
.suffix
['.i'] = swigSuffixEmitter
173 java_file
.add_action('.i', SwigAction
)
174 java_file
.add_emitter('.i', _swigEmitter
)
176 from SCons
.Platform
.mingw
import MINGW_DEFAULT_PATHS
177 from SCons
.Platform
.cygwin
import CYGWIN_DEFAULT_PATHS
178 from SCons
.Platform
.win32
import CHOCO_DEFAULT_PATH
180 if sys
.platform
== 'win32':
181 swig
= SCons
.Tool
.find_program_path(env
, 'swig', default_paths
=MINGW_DEFAULT_PATHS
+ CYGWIN_DEFAULT_PATHS
+ CHOCO_DEFAULT_PATH
)
183 swig_bin_dir
= os
.path
.dirname(swig
)
184 env
.AppendENVPath('PATH', swig_bin_dir
)
187 SCons
.Warnings
.SConsWarning
,
188 'swig tool requested, but binary not found in ENV PATH'
191 if 'SWIG' not in env
:
192 env
['SWIG'] = env
.Detect(swigs
) or swigs
[0]
194 env
['SWIGVERSION'] = _get_swig_version(env
, env
['SWIG'])
195 env
['SWIGFLAGS'] = SCons
.Util
.CLVar('')
196 env
['SWIGDIRECTORSUFFIX'] = '_wrap.h'
197 env
['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX'
198 env
['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX'
199 env
['_SWIGOUTDIR'] = r
'${"-outdir \"%s\"" % SWIGOUTDIR}'
201 env
['SWIGINCPREFIX'] = '-I'
202 env
['SWIGINCSUFFIX'] = ''
203 env
['_SWIGINCFLAGS'] = '${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX,' \
204 '__env__, RDirs, TARGET, SOURCE, affect_signature=False)}'
205 env
['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES'
208 swig
= env
.get('SWIG') or env
.Detect(['swig'])
213 # indent-tabs-mode:nil
215 # vim: set expandtab tabstop=4 shiftwidth=4: