8000 Merge pull request #4421 from meltingwax/meltingwax/4382 · numpy/numpy@94a1c20 · GitHub
[go: up one dir, main page]

Skip to content

Commit 94a1c20

Browse files
committed
Merge pull request #4421 from meltingwax/meltingwax/4382
BUG: Fix support for builds with directories that contain whitespace
2 parents 2e2bd93 + 50bf6df commit 94a1c20

File tree

5 files changed

+89
-23
lines changed

5 files changed

+89
-23
lines changed

numpy/distutils/fcompiler/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from numpy.distutils.ccompiler import CCompiler, gen_lib_options
3939
from numpy.distutils import log
4040
from numpy.distutils.misc_util import is_string, all_strings, is_sequence, \
41-
make_temp_file, get_shared_lib_extension
41+
make_temp_file, get_shared_lib_extension, quote
4242
from numpy.distutils.environment import EnvironmentConfig
4343
from numpy.distutils.exec_command import find_executable
4444
from numpy.distutils.compat import get_exception
@@ -582,12 +582,12 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
582582
% (self.__class__.__name__, src))
583583
extra_compile_args = self.extra_f90_compile_args or []
584584
if self.object_switch[-1]==' ':
585-
o_args = [self.object_switch.strip(), obj]
585+
o_args = [self.object_switch.strip(), quote(obj)]
586586
else:
587-
o_args = [self.object_switch.strip()+obj]
587+
o_args = [self.object_switch.strip() + quote(obj)]
588588

589589
assert self.compile_switch.strip()
590-
s_args = [self.compile_switch, src]
590+
s_args = [self.compile_switch, quote(src)]
591591

592592
if extra_compile_args:
593593
log.info('extra %s options: %r' \
@@ -659,6 +659,7 @@ def link(self, target_desc, objects,
659659
else:
660660
ld_args = objects + self.objects
661661
ld_args = ld_args + lib_opts + o_args
662+
ld_args = [quote(ld_arg) for ld_arg in ld_args]
662663
if debug:
663664
ld_args[:0] = ['-g']
664665
if extra_preargs:
@@ -987,3 +988,4 @@ def get_f77flags(src):
987988

988989
if __name__ == '__main__':
989990
show_fcompilers()
991+

numpy/distutils/misc_util.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
import distutils
1515
from distutils.errors import DistutilsError
1616

17+
try:
18+
from pipes import quote
19+
except ImportError:
20+
from shlex import quote
21+
1722
try:
1823
set
1924
except NameError:
@@ -31,7 +36,8 @@
3136
'get_script_files', 'get_lib_source_files', 'get_data_files',
3237
'dot_join', 'get_frame', 'minrelpath', 'njoin',
3338
'is_sequence', 'is_string', 'as_list', 'gpaths', 'get_language',
34-
'quote_args', 'get_build_architecture', 'get_info', 'get_pkg_info']
39+
'quote_args', 'quote', 'get_build_architecture', 'get_info',
40+
'get_pkg_info']
3541

3642
class InstallableLib(object):
3743
"""

numpy/distutils/npy_pkg_config.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
else:
1111
from configparser import ConfigParser, SafeConfigParser, NoOptionError
1212

13+
from numpy.distutils.misc_util import quote
14+
1315
__all__ = ['FormatError', 'PkgNotFound', 'LibraryInfo', 'VariableSet',
1416
'read_config', 'parse_flags']
1517

@@ -56,7 +58,7 @@ def parse_flags(line):
5658
* 'ignored'
5759
5860
"""
59-
lexer = shlex.shlex(line)
61+
lexer = shlex.shlex(line, posix=True)
6062
lexer.whitespace_split = True
6163

6264
d = {'include_dirs': [], 'library_dirs': [], 'libraries': [],
@@ -88,8 +90,6 @@ def next_token(t):
8890

8991
return d
9092

91-
def _escape_backslash(val):
92-
return val.replace('\\', '\\\\')
9393

9494
class LibraryInfo(object):
9595
"""
@@ -147,11 +147,11 @@ def sections(self):
147147

148148
def cflags(self, section="default"):
149149
val = self.vars.interpolate(self._sections[section]['cflags'])
150-
return _escape_backslash(val)
150+
return quote(val)
151151

152152
def libs(self, section="default"):
153153
val = self.vars.interpolate(self._sections[section]['libs'])
154-
return _escape_backslash(val)
154+
return quote(val)
155155

156156
def __str__(self):
157157
m = ['Name: %s' % self.name]
@@ -289,7 +289,7 @@ def parse_config(filename, dirs=None):
289289
vars = {}
290290
if config.has_section('variables'):
291291
for name, value in config.items("variables"):
292-
vars[name] = _escape_backslash(value)
292+
vars[name] = quote(value)
293293

294294
# Parse "normal" sections
295295
secs = [s for s in config.sections() if not s in ['meta', 'variables']]
@@ -338,7 +338,7 @@ def _read_config(f):
338338
(pkgname, meta["name"]))
339339

340340
mod = sys.modules[pkgname]
341-
vars["pkgdir"] = _escape_backslash(os.path.dirname(mod.__file__))
341+
vars["pkgdir"] = quote(os.path.dirname(mod.__file__))
342342

343343
return LibraryInfo(name=meta["name"], description=meta["description"],
344344
version=meta["version"], sections=sections, vars=VariableSet(vars))

numpy/distutils/tests/test_npy_pkg_config.py

Lines changed: 66 additions & 9 deletions
10000
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import division, absolute_import, print_function
22

33
import os
4+
import shlex
45
from tempfile import mkstemp
56

67
from numpy.testing import *
@@ -38,6 +39,12 @@
3839
'version': '0.1', 'name': 'foo'}
3940

4041
class TestLibraryInfo(TestCase):
42+
43+
def assertLexEqual(self, str1, str2):
44+
# Use shlex.split for comparison because it is above quotes
45+
# eg: shlex.split("'abc'") == shlex.split("abc")
46+
return shlex.split(str1) == shlex.split(str2)
47+
4148
def test_simple(self):
4249
fd, filename = mkstemp('foo.ini')
4350
try:
@@ -48,10 +55,10 @@ def test_simple(self):
4855
os.close(fd)
4956

5057
out = read_config(pkg)
51-
self.assertTrue(out.cflags() == simple_d['cflags'])
52-
self.assertTrue(out.libs() == simple_d['libflags'])
53-
self.assertTrue(out.name == simple_d['name'])
54-
self.assertTrue(out.version == simple_d['version'])
58+
self.assertLexEqual(out.cflags(), simple_d['cflags'])
59+
self.assertLexEqual(out.libs(), simple_d['libflags'])
60+
self.assertEqual(out.name, simple_d['name'])
61+
self.assertEqual(out.version, simple_d['version'])
5562
finally:
5663
os.remove(filename)
5764

@@ -65,13 +72,13 @@ def test_simple_variable(self):
6572
os.close(fd)
6673

6774
out = read_config(pkg)
68-
self.assertTrue(out.cflags() == simple_variable_d['cflags'])
69-
self.assertTrue(out.libs() == simple_variable_d['libflags'])
70-
self.assertTrue(out.name == simple_variable_d['name'])
71-
self.assertTrue(out.version == simple_variable_d['version'])
75+
self.assertLexEqual(out.cflags(), simple_variable_d['cflags'])
76+
self.assertLexEqual(out.libs(), simple_variable_d['libflags'])
77+
self.assertEqual(out.name, simple_variable_d['name'])
78+
self.assertEqual(out.version, simple_variable_d['version'])
7279

7380
out.vars['prefix'] = '/Users/david'
74-
self.assertTrue(out.cflags() == '-I/Users/david/include')
81+
self.assertLexEqual(out.cflags(), '-I/Users/david/include')
7582
finally:
7683
os.remove(filename)
7784

@@ -88,6 +95,28 @@ def test_simple_cflags(self):
8895
self.assertTrue(d['include_dirs'] == ['/usr/include'])
8996
self.assertTrue(d['macros'] == ['FOO'])
9097

98+
def test_quotes_cflags(self):
99+
d = parse_flags("-I'/usr/foo bar/include' -DFOO")
100+
self.assertTrue(d['include_dirs'] == ['/usr/foo bar/include'])
101+
self.assertTrue(d['macros'] == ['FOO'])
102+
103+
d = parse_flags("-I/usr/'foo bar'/include -DFOO")
104+
self.assertTrue(d['include_dirs'] == ['/usr/foo bar/include'])
105+
self.assertTrue(d['macros'] == ['FOO'])
106+
107+
d = parse_flags("'-I/usr/foo bar'/include -DFOO")
108+
self.assertTrue(d['include_dirs'] == ['/usr/foo bar/include'])
109+
self.assertTrue(d['macros'] == ['FOO'])
110+
111+
def test_escaping_cflags(self):
112+
d = parse_flags("-I/usr/foo\\ bar/include -DFOO")
113+
self.assertTrue(d['include_dirs'] == ['/usr/foo bar/include'])
114+
self.assertTrue(d['macros'] == ['FOO'])
115+
116+
d = parse_flags(r"-I/usr/foo\ bar/include -DFOO")
117+
self.assertTrue(d['include_dirs'] == ['/usr/foo bar/include'])
118+
self.assertTrue(d['macros'] == ['FOO'])
119+
91120
def test_simple_lflags(self):
92121
d = parse_flags("-L/usr/lib -lfoo -L/usr/lib -lbar")
93122
self.assertTrue(d['library_dirs'] == ['/usr/lib', '/usr/lib'])
@@ -96,3 +125,31 @@ def test_simple_lflags(self):
96125
d = parse_flags("-L /usr/lib -lfoo -L/usr/lib -lbar")
97126
self.assertTrue(d['library_dirs'] == ['/usr/lib', '/usr/lib'])
98127
self.assertTrue(d['libraries'] == ['foo', 'bar'])
128+
129+
def test_quotes_lflags(self):
130+
d = parse_flags("-L'/usr/foo bar' -lfoo -L/usr/lib -lbar")
131+
self.assertTrue(d['library_dirs'] == ['/usr/foo bar', '/usr/lib'])
132+
133+
d = parse_flags("-L/usr/'foo bar' -lfoo -L/usr/lib -lbar")
134+
self.assertTrue(d['library_dirs'] == ['/usr/foo bar', '/usr/lib'])
135+
136+
d = parse_flags("\"-L/usr/foo bar\" -lfoo -L/usr/lib -lbar")
137+
self.assertTrue(d['library_dirs'] == ['/usr/foo bar', '/usr/lib'])
138+
139+
d = parse_flags("\"-L/usr/foo bar/baz buz\" -lfoo -L/usr/lib -lbar")
140+
self.assertTrue(d['library_dirs'] == ['/usr/foo bar/baz buz', '/usr/lib'])
141+
142+
def test_escaping_lflags(self):
143+
d = parse_flags("-L/usr/foo\\ bar -lfoo -L/usr/lib -lbar")
144+
self.assertTrue(d['library_dirs'] == ['/usr/foo bar', '/usr/lib'])
145+
146+
d = parse_flags(r"-L/usr/foo\ bar -lfoo -L/usr/lib -lbar")
147+
self.assertTrue(d['library_dirs'] == ['/usr/foo bar', '/usr/lib'])
148+
149+
def test_odd_characters_lflags(self):
150+
# tab in directory name
151+
d = parse_flags('-L/usr/"foo\tbar" -lfoo -L/usr/lib -lbar')
152+
self.assertTrue(d['library_dirs'] == ['/usr/foo\tbar', '/usr/lib'])
153+
154+
d = parse_flags("-L/usr/foo\\\tbar -lfoo -L/usr/lib -lbar")
155+
self.assertTrue(d['library_dirs'] == ['/usr/foo\tbar', '/usr/lib'])

numpy/distutils/unixccompiler.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from distutils.unixccompiler import *
1111
from numpy.distutils.ccompiler import replace_method
1212
from numpy.distutils.compat import get_exception
13+
from numpy.distutils.misc_util import quote_args, quote
1314

1415
if sys.version_info[0] < 3:
1516
from . import log
@@ -88,8 +89,8 @@ def UnixCCompiler_create_static_lib(self, objects, output_libname,
8889
display = '%s: adding %d object files to %s' % (
8990
os.path.basename(self.archiver[0]),
9091
len(objects), output_filename)
91-
self.spawn(self.archiver + [output_filename] + objects,
92-
display = display)
92+
command = self.archiver + [quote(output_filename)] + quote_args(objects)
93+
self.spawn(command, display = display)
9394

9495
# Not many Unices required ranlib anymore -- SunOS 4.x is, I
9596
# think the only major Unix that does. Maybe we need some

0 commit comments

Comments
 (0)
0