|
22 | 22 | # "libname_fmt": "-l{}",
|
23 | 23 | # "rpath_fmt": "-Wl,-rpath,{}",
|
24 | 24 | # "path_delimiter": ":",
|
25 |
| -# "exe_extension": "" |
| 25 | +# "exe_extension": "", |
| 26 | +# "source_include_dir": "" |
26 | 27 | #}"""
|
27 | 28 |
|
28 | 29 | import argparse
|
29 | 30 | import json
|
30 | 31 | import os
|
31 | 32 | import pathlib
|
| 33 | +import shutil |
32 | 34 | import subprocess
|
33 | 35 | import sys
|
| 36 | +import tempfile |
| 37 | + |
| 38 | +# run command and return status object |
| 39 | +def launch_command(cmd, descr, verbose=False): |
| 40 | + if verbose: |
| 41 | + sys.stdout.write(cmd) |
| 42 | + status = subprocess.run(cmd, capture_output=True, text=True, shell=True) |
| 43 | + if status.returncode != 0: |
| 44 | + sys.stdout.write(status.stdout) |
| 45 | + sys.stderr.write(status.stderr) |
| 46 | + raise RuntimeError("Error: {}. Command: {}".format(descr, cmd)) |
| 47 | + return status |
| 48 | + |
| 49 | +# run command and return the standard output as a string |
| 50 | +def capture_command_output(cmd, descr, verbose=False): |
| 51 | + status = launch_command(cmd, descr, verbose) |
| 52 | + return status.stdout |
| 53 | + |
34 | 54 |
|
35 | 55 | conf = json.loads(JSON_DATA_TEXT)
|
| 56 | +OUTNAME_FMT = conf['outname_fmt'] |
| 57 | +LIBDIR_FMT = conf['libdir_fmt'] |
| 58 | +LIBNAME_FMT = conf['libname_fmt'] |
| 59 | +RPATH_FMT = conf['rpath_fmt'] |
| 60 | +PATH_DELIMITER = conf['path_delimiter'] |
| 61 | +RPATHS = conf['rpaths'].split(PATH_DELIMITER) |
| 62 | +exeext = conf['exe_extension'] |
| 63 | +SOURCE_INCLUDE_DIR = conf['source_include_dir'] |
| 64 | + |
| 65 | +workdir = os.getcwd() |
| 66 | +scriptdir = pathlib.Path(os.path.dirname(os.path.abspath(__file__))) |
36 | 67 |
|
37 |
| -parser = argparse.ArgumentParser(description="Compile c++ code generated by Souffle") |
| 68 | +parser = argparse.ArgumentParser(description="Compile a C++ source file generated by Souffle") |
38 | 69 | parser.add_argument('-l', action='append', default=[], metavar='LIBNAME', dest='lib_names', type=str, help="Basename of a functors library. eg: `-l functors` => libfunctors.dll")
|
39 | 70 | parser.add_argument('-L', action='append', default=[], metavar='LIBDIR', dest='lib_dirs', type=lambda p: pathlib.Path(p).absolute(), help="Search directory for functors libraries")
|
40 | 71 | parser.add_argument('-g', action='store_true', dest='debug', help="Debug build type")
|
41 |
| -parser.add_argument('-s', action='store_true', dest='swig', help="TODO") |
42 |
| -parser.add_argument('-v', action='store_true', dest='verbose', help="Verbose") |
| 72 | +parser.add_argument('-s', metavar='VALUE', dest='swiglang', help="use SWIG interface to generate into VALUE language") |
| 73 | +parser.add_argument('-v', action='store_true', dest='verbose', help="Verbose output") |
| 74 | +parser.add_argument('-w', action='store_true', dest='warnings', help="Enable warnings") |
| 75 | +parser.add_argument('-t', action='store_true', dest='testmode', help="Build in test mode") |
43 | 76 | parser.add_argument('source', metavar='SOURCE', type=lambda p: pathlib.Path(p).absolute(), help="C++ source file")
|
44 | 77 |
|
45 | 78 | args = parser.parse_args()
|
46 | 79 |
|
| 80 | +stemname = args.source.stem |
| 81 | +dirname = args.source.parent |
| 82 | + |
47 | 83 | if not os.path.isfile(args.source):
|
48 | 84 | raise RuntimeError("Cannot open source file: '{}'".format(args.source))
|
49 | 85 |
|
| 86 | +# Check if the input file has a valid extension |
50 | 87 | extname = args.source.suffix
|
51 | 88 | if extname != ".cpp":
|
52 | 89 | raise RuntimeError("Source file is not a .cpp file: '{}'".format(args.source))
|
53 | 90 |
|
54 |
| -basename = args.source.stem |
55 |
| -dirname = args.source.parent |
56 |
| -exeext = conf['exe_extension'] |
57 |
| -exepath = pathlib.Path(dirname.joinpath("{}{}".format(basename, exeext))) |
58 |
| - |
59 |
| -OUTNAME_FMT = conf['outname_fmt'] |
60 |
| -LIBDIR_FMT = conf['libdir_fmt'] |
61 |
| -LIBNAME_FMT = conf['libname_fmt'] |
62 |
| -RPATH_FMT = conf['rpath_fmt'] |
63 |
| -PATH_DELIMITER = conf['path_delimiter'] |
64 |
| -RPATHS = conf['rpaths'].split(PATH_DELIMITER) |
| 91 | +# Search for Souffle includes directory |
| 92 | +souffle_include_dir = None |
| 93 | +if (scriptdir / "include" / "souffle").exists(): |
| 94 | + souffle_include_dir = scriptdir / "include" / "souffle" |
| 95 | +elif (scriptdir / ".." / "include" / "souffle").exists(): |
| 96 | + souffle_include_dir = scriptdir / ".." / "include" / "souffle" |
| 97 | +elif SOURCE_INCLUDE_DIR and (pathlib.Path(SOURCE_INCLUDE_DIR) / "souffle").exists(): |
| 98 | + souffle_include_dir = (pathlib.Path(SOURCE_INCLUDE_DIR) / "souffle") |
| 99 | + |
| 100 | +# Make temp folder and copy relevant files there |
| 101 | +if args.swiglang: |
| 102 | + if not (souffle_include_dir and (souffle_include_dir / "swig").exists()): |
| 103 | + raise RuntimeError("Cannot find 'souffle/swig' include directory") |
| 104 | + swig_include_dir = (souffle_include_dir / "swig") |
| 105 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 106 | + shutil.copy(swig_include_dir / "SwigInterface.h", tmpdir) |
| 107 | + shutil.copy(swig_include_dir / "SwigInterface.i", tmpdir) |
| 108 | + |
| 109 | + os.chdir(tmpdir) |
| 110 | + launch_command("swig -c++ -\"{}\" SwigInterface.i".format(args.swiglang), "SWIG generation") |
| 111 | + |
| 112 | + python_flags = capture_command_output("python3-config --cflags", "Python config") |
| 113 | + python_ldflags = capture_command_output("python3-config --ldflags", "Python config") |
| 114 | + |
| 115 | + if args.swiglang == "python": |
| 116 | + # compile swig interface and program |
| 117 | + cmd = [] |
| 118 | + cmd.append('"{}"'.format(conf['compiler'])) |
| 119 | + cmd.append("-fPIC") |
| 120 | + cmd.append("-c") |
| 121 | + cmd.append("-D__EMBEDDED_SOUFFLE__") |
| 122 | + cmd.append("SwigInterface_wrap.cxx") |
| 123 | + cmd.append(str(args.source)) |
| 124 | + cmd.append(conf['definitions']) |
| 125 | + cmd.append(conf['compile_options']) |
| 126 | + cmd.append(conf['includes']) |
| 127 | + cmd.append(conf['std_flag']) |
| 128 | + cmd.append(conf['cxx_flags']) |
| 129 | + cmd.append(python_flags) |
| 130 | + cmd = " ".join(cmd) |
| 131 | + launch_command(cmd, "Compilation of SWIG python C++") |
| 132 | + |
| 133 | + # link swig interface and program |
| 134 | + cmd = [] |
| 135 | + cmd.append('"{}"'.format(conf['compiler'])) |
| 136 | + cmd.append("-shared") |
| 137 | + cmd.append("SwigInterface_wrap.o") |
| 138 | + cmd.append("{}{}".format(stemname, ".o")) |
| 139 | + cmd.append("-o") |
| 140 | + cmd.append("_SwigInterface.so") |
| 141 | + cmd.append(conf['definitions']) |
| 142 | + cmd.append(conf['compile_options']) |
| 143 | + cmd.append(conf['includes']) |
| 144 | + cmd.append(conf['std_flag']) |
| 145 | + cmd.append(conf['cxx_flags']) |
| 146 | + cmd.append(conf['link_options']) |
| 147 | + cmd.extend(list(map(lambda rpath: RPATH_FMT.format(rpath), RPATHS))) |
| 148 | + cmd.extend(list(map(lambda libdir: LIBDIR_FMT.format(libdir), args.lib_dirs))) |
| 149 | + cmd.extend(list(map(lambda libname: LIBNAME_FMT.format(libname), args.lib_names))) |
| 150 | + cmd.append(python_ldflags) |
| 151 | + cmd = " ".join(cmd) |
| 152 | + launch_command(cmd, "Link of SWIG python C++") |
| 153 | + |
| 154 | + shutil.copy("_SwigInterface.so", workdir) |
| 155 | + shutil.copy("SwigInterface.py", workdir) |
| 156 | + |
| 157 | + elif args.swiglang == "java": |
| 158 | + raise RuntimeError("TODO: Swig Java") |
| 159 | + |
| 160 | + # move generated files to same directory as cpp file |
| 161 | + os.sys.exit(0) |
| 162 | + |
| 163 | + |
| 164 | +exepath = pathlib.Path(dirname.joinpath("{}{}".format(stemname, exeext))) |
65 | 165 |
|
66 | 166 | cmd = []
|
67 | 167 | cmd.append('"{}"'.format(conf['compiler']))
|
|
0 commit comments