Commit 6caec6aa authored by Michael Droettboom's avatar Michael Droettboom Committed by GitHub

Merge pull request #232 from rth/fortran-compilation

Support Fortran compilation with f2c; distutils suport
parents b204e0e2 04291857
...@@ -41,6 +41,7 @@ from pyodide_build import common ...@@ -41,6 +41,7 @@ from pyodide_build import common
ROOTDIR = common.ROOTDIR ROOTDIR = common.ROOTDIR
symlinks = set(['cc', 'c++', 'ld', 'ar', 'gcc']) symlinks = set(['cc', 'c++', 'ld', 'ar', 'gcc'])
symlinks = set(['cc', 'c++', 'ld', 'ar', 'gcc', 'gfortran'])
def collect_args(basename): def collect_args(basename):
...@@ -105,6 +106,52 @@ def capture_compile(args): ...@@ -105,6 +106,52 @@ def capture_compile(args):
sys.exit(result.returncode) sys.exit(result.returncode)
def f2c(args, dryrun=False):
"""Apply f2c to compilation arguments
Parameters
----------
args : iterable
input compiler arguments
dryrun : bool, default=True
if False run f2c on detected fortran files
Returns
-------
new_args : list
output compiler arguments
Examples
--------
>>> f2c(['gfortran', 'test.f'], dryrun=True)
['gfortran', 'test.c']
"""
new_args = []
found_source = False
for arg in args:
if arg.endswith('.f'):
filename = os.path.abspath(arg)
if not dryrun:
subprocess.check_call(
['f2c', os.path.basename(filename)],
cwd=os.path.dirname(filename))
new_args.append(arg[:-2] + '.c')
found_source = True
else:
new_args.append(arg)
new_args_str = ' '.join(args)
if ".so" in new_args_str and "libgfortran.so" not in new_args_str:
found_source = True
if not found_source:
print(f'f2c: source not found, skipping: {new_args_str}')
return None
return new_args
def handle_command(line, args, dryrun=False): def handle_command(line, args, dryrun=False):
"""Handle a compilation command """Handle a compilation command
...@@ -138,7 +185,13 @@ def handle_command(line, args, dryrun=False): ...@@ -138,7 +185,13 @@ def handle_command(line, args, dryrun=False):
if arg == '-print-multiarch': if arg == '-print-multiarch':
return return
if line[0] == 'ar': if line[0] == 'gfortran':
result = f2c(line)
if result is None:
return
line = result
new_args = ['emcc']
elif line[0] == 'ar':
new_args = ['emar'] new_args = ['emar']
elif line[0] == 'c++': elif line[0] == 'c++':
new_args = ['em++'] new_args = ['em++']
...@@ -171,11 +224,19 @@ def handle_command(line, args, dryrun=False): ...@@ -171,11 +224,19 @@ def handle_command(line, args, dryrun=False):
arg = re.sub(r'/python([0-9]\.[0-9]+)m', r'/python\1', arg) arg = re.sub(r'/python([0-9]\.[0-9]+)m', r'/python\1', arg)
if arg.endswith('.o'): if arg.endswith('.o'):
arg = arg[:-2] + '.bc' arg = arg[:-2] + '.bc'
if shared and arg.endswith('.so'): output = arg
elif shared and arg.endswith('.so'):
arg = arg[:-3] + '.wasm' arg = arg[:-3] + '.wasm'
output = arg output = arg
new_args.append(arg) new_args.append(arg)
# This can only be used for incremental rebuilds -- it generates
# an error during clean build of numpy
# if os.path.isfile(output):
# print('SKIPPING: ' + ' '.join(new_args))
# return
print(' '.join(new_args)) print(' '.join(new_args))
if not dryrun: if not dryrun:
...@@ -217,13 +278,22 @@ def clean_out_native_artifacts(): ...@@ -217,13 +278,22 @@ def clean_out_native_artifacts():
def install_for_distribution(args): def install_for_distribution(args):
subprocess.check_call( commands = [
[Path(args.host) / 'bin' / 'python3', Path(args.host) / 'bin' / 'python3',
'setup.py', 'setup.py',
'install', 'install',
'--skip-build', '--skip-build',
'--prefix=install', '--prefix=install',
'--old-and-unmanageable']) '--old-and-unmanageable'
]
try:
subprocess.check_call(commands)
except Exception:
print(f'Warning: {" ".join(commands)} failed with distutils, possibly '
f'due to the use of distutils that does not support the '
f'--old-and-unmanageable argument. Re-trying the install '
f'without this argument.')
subprocess.check_call(commands[:-1])
def build_wrap(args): def build_wrap(args):
......
...@@ -5,6 +5,7 @@ import sys ...@@ -5,6 +5,7 @@ import sys
sys.path.append(str(Path(__file__).parents[2])) sys.path.append(str(Path(__file__).parents[2]))
from pyodide_build.pywasmcross import handle_command # noqa: E402 from pyodide_build.pywasmcross import handle_command # noqa: E402
from pyodide_build.pywasmcross import f2c # noqa: E402
def _args_wrapper(func): def _args_wrapper(func):
...@@ -24,7 +25,7 @@ def _args_wrapper(func): ...@@ -24,7 +25,7 @@ def _args_wrapper(func):
handle_command_wrap = _args_wrapper(handle_command) handle_command_wrap = _args_wrapper(handle_command)
# TODO: add f2c here f2c_wrap = _args_wrapper(f2c)
def test_handle_command(): def test_handle_command():
...@@ -42,3 +43,11 @@ def test_handle_command(): ...@@ -42,3 +43,11 @@ def test_handle_command():
# compilation checks in numpy # compilation checks in numpy
assert handle_command_wrap('gcc /usr/file.c', args) is None assert handle_command_wrap('gcc /usr/file.c', args) is None
def test_f2c():
assert f2c_wrap('gfortran test.f') == 'gfortran test.c'
assert f2c_wrap('gcc test.c') is None
assert f2c_wrap('gfortran --version') is None
assert f2c_wrap('gfortran --shared -c test.o -o test.so') == \
'gfortran --shared -c test.o -o test.so'
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment