Building slycot from source.
Change-Id: I2e9df7a50749818bd4526161fbc50bca08e255e8
diff --git a/WORKSPACE b/WORKSPACE
index a37e1d4..999c66a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -4,6 +4,18 @@
build_file = 'debian/usr.BUILD',
)
+new_git_repository(
+ name = 'slycot_repo',
+ remote = 'https://github.com/avventi/Slycot.git',
+ build_file = 'debian/slycot.BUILD',
+ commit = '5af5f283cb23cbe23c4dfea4d5e56071bdbd6e70',
+)
+
+bind(
+ name = 'slycot',
+ actual = '@slycot_repo//:slycot',
+)
+
# TODO(brian): Make these point to something which isn't hard-coded to come off
# the host system...
bind(
diff --git a/debian/slycot.BUILD b/debian/slycot.BUILD
new file mode 100644
index 0000000..64eb358
--- /dev/null
+++ b/debian/slycot.BUILD
@@ -0,0 +1,96 @@
+# TODO(austin): I bet this is wrong.
+licenses(['restricted'])
+
+load('/tools/build_rules/fortran', 'fortran_library')
+
+# We can't create _wrapper.so in the slycot folder, and can't move it.
+# The best way I found to do this is to modify _wrapper.pyf to instead generate
+# a _fortranwrapper.so library, and then place a _wrapper.py file in slycot/
+# which loads _fortranwrapper from the correct location. This means that I
+# don't need to modify the repository.
+genrule(
+ name = '_fortranwrapper_pyf',
+ srcs = ['slycot/src/_wrapper.pyf'],
+ outs = ['slycot/src/_fortranwrapper.pyf'],
+ cmd = 'cat $(SRCS) | sed \'s/_wrapper/_fortranwrapper/\' > $(OUTS)'
+)
+
+# Now generate the module wrapper.
+genrule(
+ name = '_fortranwrappermodule',
+ srcs = [
+ 'slycot/src/analysis.pyf',
+ 'slycot/src/synthesis.pyf',
+ 'slycot/src/_fortranwrapper.pyf',
+ 'slycot/src/math.pyf',
+ 'slycot/src/transform.pyf',
+ ],
+ outs = ['_fortranwrappermodule.c'],
+ cmd = '/usr/bin/python /usr/bin/f2py $(location :slycot/src/_fortranwrapper.pyf) --include-paths external/slycot_repo/slycot/src/ --coutput $(OUTS)',
+)
+
+# Build it.
+cc_library(
+ name = 'slycot_c',
+ srcs = [
+ ':_fortranwrappermodule',
+ ],
+ deps = [
+ ':fortran_files',
+ '@usr_repo//:python2.7_lib',
+ '@usr_repo//:python2.7_f2py',
+ ],
+ copts = [
+ '-Wno-error',
+ '-Wno-incompatible-pointer-types-discards-qualifiers',
+ '-Wno-cast-align',
+ '-Wno-unused-parameter',
+ '-Wno-missing-field-initializers',
+ '-Wno-unused-function',
+ ],
+)
+
+# Now actually build the fortran files.
+fortran_library(
+ name = 'fortran_files',
+ srcs = glob(['slycot/src/*.f']),
+)
+
+# Link it all together. Make sure it is dynamically linked since I don't know
+# how to build the fortran files in statically to a single .so yet, and I'm not
+# sure bazel does either.
+cc_binary(
+ name = '_fortranwrapper.so',
+ deps = [
+ ':fortran_files',
+ ':slycot_c',
+ ],
+ linkopts = ['-shared', '-lblas', '-llapack'],
+ linkstatic=0,
+)
+
+# Generate the _wrapper file which loads _fortranwrapper and pretends.
+genrule(
+ name = '_wrapper',
+ outs = ['slycot/_wrapper.py'],
+ cmd = 'echo "from _fortranwrapper import *" > $(OUTS)',
+ output_to_bindir=1,
+)
+
+# Now present a python library for slycot
+py_library(
+ name = 'slycot',
+ srcs = [
+ 'slycot/analysis.py',
+ 'slycot/examples.py',
+ 'slycot/__init__.py',
+ 'slycot/math.py',
+ 'slycot/synthesis.py',
+ 'slycot/transform.py',
+ ':_wrapper',
+ ],
+ data = [
+ ':_fortranwrapper.so',
+ ],
+ visibility = ['//visibility:public'],
+)
diff --git a/debian/usr.BUILD b/debian/usr.BUILD
index e50da1a..ac354cc 100644
--- a/debian/usr.BUILD
+++ b/debian/usr.BUILD
@@ -24,3 +24,64 @@
cc_library(
name = 'libpthread',
)
+
+cc_library(
+ name = 'python3.4_lib',
+ hdrs = glob(['include/python3.4m/**/*.h']),
+ includes = [
+ 'include/python3.4m/',
+ ],
+ visibility = ['//visibility:public'],
+)
+
+cc_library(
+ name = 'python3.4_f2py',
+ srcs = [
+ 'lib/python3/dist-packages/numpy/f2py/src/fortranobject.c',
+ ],
+ hdrs = [
+ 'lib/python3/dist-packages/numpy/f2py/src/fortranobject.h',
+ ],
+ copts = [
+ '-Wno-error',
+ ],
+ includes = [
+ 'lib/python3/dist-packages/numpy/f2py/src/',
+ ],
+ deps = [
+ ':python3.4_lib',
+ ],
+ visibility = ['//visibility:public'],
+)
+
+cc_library(
+ name = 'python2.7_lib',
+ hdrs = glob(['include/python2.7/**/*.h']),
+ srcs = [
+ 'lib/x86_64-linux-gnu/libpython2.7.so',
+ ],
+ includes = [
+ 'include/python2.7/',
+ ],
+ visibility = ['//visibility:public'],
+)
+
+cc_library(
+ name = 'python2.7_f2py',
+ srcs = [
+ 'lib/python2.7/dist-packages/numpy/f2py/src/fortranobject.c',
+ ],
+ hdrs = [
+ 'lib/python2.7/dist-packages/numpy/f2py/src/fortranobject.h',
+ ],
+ copts = [
+ '-Wno-error',
+ ],
+ includes = [
+ 'lib/python2.7/dist-packages/numpy/f2py/src/',
+ ],
+ deps = [
+ ':python2.7_lib',
+ ],
+ visibility = ['//visibility:public'],
+)
diff --git a/frc971/control_loops/python/BUILD b/frc971/control_loops/python/BUILD
index 747b49c..b79d676 100644
--- a/frc971/control_loops/python/BUILD
+++ b/frc971/control_loops/python/BUILD
@@ -8,4 +8,7 @@
'polytope.py',
'libcdd.py',
],
+ deps = [
+ '//external:slycot',
+ ],
)
diff --git a/tools/build_rules/fortran.bzl b/tools/build_rules/fortran.bzl
new file mode 100644
index 0000000..4de4430
--- /dev/null
+++ b/tools/build_rules/fortran.bzl
@@ -0,0 +1,78 @@
+def _single_fortran_object_impl(ctx):
+ toolchain_cflags = (ctx.fragments.cpp.compiler_options([]) +
+ ctx.fragments.cpp.c_options +
+ ctx.fragments.cpp.unfiltered_compiler_options([]) + ['-fPIC'])
+
+ cmd = toolchain_cflags + ['-c', ctx.file.src.path, '-o', ctx.outputs.pic_o.path]
+ filtered_cmd = []
+ # Strip out the C/C++ specific flags.
+ exclude_flags = ['-fcolor-diagnostics',
+ '-Wswitch-enum',
+ '-Wpointer-arith',
+ '-Wcast-qual',
+ '-Wwrite-strings',
+ '-Wsign-compare',
+ '-Wformat=2',
+ '-Werror',
+ '-Wno-builtin-macro-redefined',
+ '-D__has_feature(x)=0']
+
+ for flag in cmd:
+ if flag not in exclude_flags:
+ filtered_cmd.append(flag)
+
+ ctx.action(
+ inputs = [ctx.file.src],
+ outputs = [ctx.outputs.pic_o],
+ mnemonic = "Fortran",
+ executable = ctx.fragments.cpp.compiler_executable,
+ arguments = filtered_cmd,
+ progress_message = 'Building %s' % ctx.outputs.pic_o.short_path,
+ )
+
+def _define_fortran_output(attrs):
+ if not attrs.src.name.endswith('.f'):
+ fail('Fortran files must end in \'.f\'', 'src')
+
+ fortran_file_base = attrs.src.name[:-2]
+ return {
+ 'pic_o': fortran_file_base + '.pic.o',
+ }
+
+
+_single_fortran_object = rule(
+ implementation = _single_fortran_object_impl,
+ attrs = {
+ 'src': attr.label(single_file=True, allow_files=FileType(['.f'])),
+ 'cc_libs': attr.label_list(providers=['cc']),
+ },
+ outputs = _define_fortran_output,
+ fragments = [
+ 'cpp',
+ ],
+)
+
+def fortran_library(name, srcs, deps = [], visibility = None):
+ """Builds a shared library from a set of fortran files.
+
+ Args:
+ srcs: list of fortran files ending in .f
+ deps: cc_library or fortran_library dependencies.
+ """
+ pic_o_files = []
+ for src in srcs:
+ pic_o_file = src[:-2] + '.pic.o'
+ _single_fortran_object(name=name + '_' + pic_o_file,
+ src=src,
+ visibility=['//visibility:private'])
+ pic_o_files.append(pic_o_file)
+
+ native.cc_library(
+ name = name,
+ deps = deps,
+ srcs = pic_o_files,
+ linkopts = [
+ '-lgfortran',
+ ],
+ visibility = visibility,
+ )